home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Tool Chest / Development Tools & Languages / Dylan Related / Mindy-1.1 (sources only) / mindy-1.1 / libraries / streams / streams-imp.dylan < prev    next >
Encoding:
Text File  |  1994-08-25  |  96.1 KB  |  2,780 lines  |  [TEXT/ttxt]

  1. module: Streams
  2. author: chiles@cs.cmu.edu
  3. synopsis: This file implements streams for the Gwydion implementation of Dylan.
  4. copyright: Copyright (C) 1994, Carnegie Mellon University
  5.        All rights reserved.
  6.        This code was produced by the Gwydion Project at Carnegie Mellon
  7.        University.  If you are interested in using this code, contact
  8.        "Scott.Fahlman@cs.cmu.edu" (Internet).
  9. rcs-header: $Header: streams-imp.dylan,v 1.16 94/08/24 01:04:31 chiles Exp $
  10.  
  11. //======================================================================
  12. //
  13. // Copyright (c) 1994  Carnegie Mellon University
  14. // All rights reserved.
  15. // 
  16. // Use and copying of this software and preparation of derivative
  17. // works based on this software are permitted, including commercial
  18. // use, provided that the following conditions are observed:
  19. // 
  20. // 1. This copyright notice must be retained in full on any copies
  21. //    and on appropriate parts of any derivative works.
  22. // 2. Documentation (paper or online) accompanying any system that
  23. //    incorporates this software, or any part of it, must acknowledge
  24. //    the contribution of the Gwydion Project at Carnegie Mellon
  25. //    University.
  26. // 
  27. // This software is made available "as is".  Neither the authors nor
  28. // Carnegie Mellon University make any warranty about the software,
  29. // its performance, or its conformity to any specification.
  30. // 
  31. // Bug reports, questions, comments, and suggestions should be sent by
  32. // E-mail to the Internet address "gwydion-bugs@cs.cmu.edu".
  33. //
  34. //======================================================================
  35.  
  36.  
  37.  
  38. //// Constants.
  39. ////
  40.  
  41. define constant $maximum-buffer-size = $maximum-fixed-integer;
  42.  
  43. define constant $default-buffer-size = 2000;
  44.  
  45. define constant <buffer-index> =
  46.   limited(<fixed-integer>, min: 0, max: $maximum-buffer-size);
  47.  
  48.  
  49.  
  50. //// Some classes (including conditions).
  51. ////
  52.  
  53. /// <stream> Class -- Exported.
  54. ///
  55. /// All other streams inherit from this class.
  56. ///
  57. /// Though all streams have buffers, or appear to have buffers, subclasses
  58. /// of the <stream> class cannot inherit the buffer slot from this class
  59. /// because the stream interface makes no provision for implementors of new
  60. /// streams to fetch the buffer.
  61. ///
  62. define abstract class <stream> (<object>)
  63.   //
  64.   // This will do for now just to make sure the stream is not already held.
  65.   // Yes, users of this module that implement their own streams will get this
  66.   // slot and be unable to use it due to no interface to it.  Oh well.
  67.   slot stream-lock :: <multilock>,
  68.     init-function: method () make(<multilock>) end;
  69.   //
  70.   // Yes, users of this module that implement their own streams will get this
  71.   // slot and be unable to use it due to no interface to it.  Oh well.
  72.   slot buffer-locked? :: <boolean>, init-value: #f;
  73. end class;
  74.  
  75. /// <random-access-stream> Class -- Exported.
  76. ///
  77. /// All required streams inherit from this class.
  78. ///
  79. define abstract class <random-access-stream> (<stream>)
  80. end class;
  81.  
  82.  
  83. ///
  84. /// Conditions.
  85. ///
  86.  
  87. /// These are all exported.
  88. ///
  89.  
  90. define class <end-of-file> (<error>)
  91.   slot end-of-file-stream :: <stream>, init-keyword: #"stream";
  92. end class;
  93.  
  94. define class <file-not-found> (<error>)
  95.   slot file-not-found-filename :: <string>, init-keyword: #"filename";
  96. end class;
  97.  
  98. define class <file-exists> (<error>)
  99.   slot file-exists-filename :: <string>, init-keyword: #"filename";
  100. end class;
  101.  
  102.  
  103.  
  104. //// Internal protocol for streams.
  105. ////
  106.  
  107. ///
  108. /// Stream locking.
  109. ///
  110.  
  111. /// stream-locked? -- Internal Interface.
  112. ///
  113. /// This function returns whether the stream is currently held (in use) by
  114. /// the application.  Only one thread of the application may use the stream
  115. /// at one time.  Having the stream locked is different than holding the
  116. /// buffer.  The stream must be locked before a thread can get the buffer,
  117. /// and the stream may be locked across multiple calls to functions that get
  118. /// and release the buffer.  Streams use a <multilock> from the Threads
  119. /// module of the Dylan library so that a single thread may repeatedly lock
  120. /// the stream.
  121. ///
  122. define generic stream-locked? (stream :: <stream>) => locked? :: <boolean>;
  123.  
  124. define method stream-locked? (stream :: <stream>) => locked? :: <boolean>;
  125.   locked?(stream.stream-lock);
  126. end method;
  127.  
  128. /// lock-stream -- Internal Interface.
  129. ///
  130. define generic lock-stream (stream :: <stream>) => meaningless :: singleton(#f);
  131.  
  132. define method lock-stream (stream :: <stream>) => meaningless :: singleton(#f);
  133.   grab-lock(stream.stream-lock);
  134. end method;
  135.  
  136. /// unlock-stream -- Internal Interface.
  137. ///
  138. define generic unlock-stream (stream :: <stream>)
  139.     => meaningless :: singleton(#f);
  140.  
  141. define method unlock-stream (stream :: <stream>)
  142.     => meaningless :: singleton(#f);
  143.   release-lock(stream.stream-lock);
  144. end method;
  145.  
  146.  
  147. ///
  148. /// Buffer locking.
  149. /// 
  150.  
  151. /// buffer-locked? -- Internal Interface.
  152. ///
  153. /// This function returns whether the buffer is currently in use.  Only one
  154. /// thread of the application may use the buffer at one time, which is
  155. /// enforced by locking the stream.  Functions that lock the stream and then
  156. /// get the buffer cannot call other functions that get the buffer, unless
  157. /// the first function releases the buffer before calling the second
  158. /// function; buffers are NOT locked with multilocking semantics.
  159. ///
  160. /// This function is implemented as a slot in the <stream> class.
  161. /// Applications must lock the stream before calling this function.
  162. ///
  163. define generic buffer-locked? (stream :: <stream>) => locked? :: <boolean>;
  164.  
  165. /// buffer-locked?-setter -- Internal Interface.
  166. ///
  167. /// This function is implemented as a slot in the <stream> class.
  168. /// Applications must lock the stream before calling this function.
  169. ///
  170. define generic buffer-locked?-setter (value :: <boolean>, stream :: <stream>)
  171.     => locked? :: <boolean>;
  172.  
  173.  
  174. ///
  175. /// Buffer access, next values, and stop values.
  176. ///
  177.  
  178. /// buffer -- Internal Interface.
  179. ///
  180. /// This function returns the buffer or #f.  Streams should set the buffer to
  181. /// #f when the stream is closed.  This function can be a test for whether the
  182. /// stream is still open.
  183. ///
  184. /// This function is typically implemented as a slot in the stream's class,
  185. /// but some streams may want to implement it virtually (on demand) when
  186. /// users insist on using the stream's buffer directly.
  187. ///
  188. define generic buffer (stream :: <stream>)
  189.     => buffer :: union(<buffer>, singleton(#f));
  190.  
  191. /// buffer-setter -- Internal Interface.
  192. ///
  193. define generic buffer-setter (value :: union(<buffer>, singleton(#f)),
  194.                   stream :: <stream>)
  195.     => value :: union(<buffer>, singleton(#f));
  196.  
  197. /// buffer-next -- Internal Interface.
  198. ///
  199. /// This function is implemented as slots in class definitions.  See the
  200. /// class definitiond for what the return value means.
  201. ///
  202. define generic buffer-next (stream :: <stream>) => next :: <buffer-index>;
  203.  
  204. /// buffer-stop -- Internal Interface.
  205. ///
  206. /// This function is implemented as slots in class definitions.  See the
  207. /// class definitiond for what the return value means.
  208. ///
  209. define generic buffer-stop (stream :: <stream>) => stop :: <buffer-index>;
  210.  
  211.  
  212. ///
  213. /// Output stream registration and forcing output upon Application exit.
  214. ///
  215.  
  216. /// This lock isolates access to *output-streams*.
  217. ///
  218. define constant output-stream-registry-lock = make(<semaphore>);
  219.  
  220. /// This list contains all open output streams.  There is a function
  221. /// registered on the exist hook that forces output on all streams when the
  222. /// application exits.
  223. ///
  224. define variable *output-streams* = #();
  225.  
  226. /// register-output-stream -- Internal Interface.
  227. ///
  228. /// This function registers output functions for the purpose of
  229. /// synchronizing their output when an application exits.  The same registry
  230. /// of streams could be used by a demon thread that periodically wakes up
  231. /// and forces output on streams.
  232. ///
  233. define method register-output-stream (stream :: <stream>) => stream :: <stream>;
  234.   grab-lock(output-stream-registry-lock);
  235.   *output-streams* := pair(stream, *output-streams*);
  236.   release-lock(output-stream-registry-lock);
  237.   stream;
  238. end method;
  239.  
  240. /// unregister-output-stream -- Internal Interface.
  241. ///
  242. /// This function removes stream from *output-streams*.
  243. ///
  244. define method unregister-output-stream (stream :: <stream>)
  245.     => stream :: <stream>;
  246.   grab-lock(output-stream-registry-lock);
  247.   *output-streams* := remove!(*output-streams*, stream);
  248.   release-lock(output-stream-registry-lock);
  249.   stream;
  250. end method;
  251.  
  252. /// Register a function on the application exit hook.  This function forces
  253. /// output for every output stream.  There's no reason to isolate access to
  254. /// *output-streams* because exit functions run one at a time in the only
  255. /// remaining thread.
  256. ///
  257. on-exit(method ()
  258.       for (stream in *output-streams*)
  259.         synchronize-output(stream);
  260.       end;
  261.       end);
  262.  
  263.  
  264.  
  265. //// Stream Extension Protocol.
  266. ////
  267.  
  268. /// All of these are exported.
  269. ///
  270.  
  271. define generic close (stream :: <stream>) => meaningless :: singleton(#f);
  272.  
  273. define generic stream-extension-get-input-buffer (stream :: <stream>)
  274.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  275.  
  276. define generic stream-extension-release-input-buffer
  277.     (stream :: <stream>, next :: <buffer-index>, stop :: <buffer-index>)
  278.     => meaningless :: singleton(#f);
  279.  
  280. define generic fill-input-buffer (stream :: <stream>, start :: <buffer-index>)
  281.     => stop :: <buffer-index>;
  282.  
  283. define generic input-available-at-source? (stream :: <stream>)
  284.     => available? :: <boolean>;
  285.  
  286. define generic stream-extension-get-output-buffer (stream :: <stream>)
  287.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  288.  
  289. define generic stream-extension-release-output-buffer
  290.     (stream :: <stream>, next :: <buffer-index>)
  291.     => meaningless :: singleton(#f);
  292.  
  293. define generic force-output-buffer (stream :: <stream>, stop :: <buffer-index>)
  294.     => meaningless :: singleton(#f);
  295.  
  296. define generic synchronize-output-buffer
  297.     (stream :: <stream>, stop :: <buffer-index>)
  298.     => meaningless :: singleton(#f);
  299.  
  300.  
  301.  
  302. //// Basic I/O Protocol.
  303. ////
  304.  
  305. /// All of these are exported.
  306. ///
  307.  
  308. define generic read-byte (stream :: <stream>,
  309.               #key signal-eof?: :: <boolean>) // = #t
  310.     => byte :: union(<byte>, singleton(#f));
  311.  
  312. define method read-byte (stream :: <stream>,
  313.              #key signal-eof? :: <boolean> = #t)
  314.     => byte :: union(<byte>, singleton(#f));
  315.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  316.     = get-input-buffer(stream);
  317.   if (next == stop)
  318.     stop := fill-input-buffer(stream, 0);
  319.     next := 0;
  320.   end;
  321.   if (stop ~= 0)
  322.     let res = buf[next];
  323.     release-input-buffer(stream, next + 1, stop);
  324.     res;
  325.   elseif (signal-eof?)
  326.     release-input-buffer(stream, 0, 0);
  327.     error(make(<end-of-file>, stream: stream));
  328.   else
  329.     release-input-buffer(stream, 0, 0);
  330.     #f;
  331.   end;
  332. end method;
  333.  
  334.  
  335. define generic peek-byte (stream :: <stream>)
  336.     => byte :: union(<byte>, singleton(#f));
  337.  
  338. define method peek-byte (stream :: <stream>)
  339.     => byte :: union(<byte>, singleton(#f));
  340.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  341.     = get-input-buffer(stream);
  342.   if (next == stop)
  343.     stop := fill-input-buffer(stream, 0);
  344.     next := 0;
  345.   end;
  346.   if (stop ~= 0)
  347.     let res = buf[next];
  348.     release-input-buffer(stream, next, stop);
  349.     res;
  350.   else
  351.     release-input-buffer(stream, 0, 0);
  352.     #f;
  353.   end;
  354. end method;
  355.  
  356.  
  357. define generic read-line (stream :: <stream>,
  358.               #key signal-eof?: :: <boolean>) // = #t
  359.     => (result :: union(<string>, singleton(#f)), eof? :: <boolean>);
  360.  
  361. /// This could be a literal constant in the following method definition, but
  362. /// Dylan failed to incorporate any means for cleanly identifying non-printing
  363. /// characters in character and string literals.  I don't want to use my
  364. /// editor to quote non-printing characters into my program's source.
  365. ///
  366. define constant $newline-byte = as(<byte>, '\n');
  367.  
  368. /// This cannot use a big global buffer to build the result because of
  369. /// thread-safety.  The intermediate result consing should be rare or minimal,
  370. /// assuming a reasonable relationship between line lengths and the buffer's
  371. /// length.
  372. ///
  373. define method read-line (stream :: <stream>,
  374.              #key signal-eof? :: <boolean> = #t)
  375.     => (result :: union(<string>, singleton(#f)), eof? :: <boolean>);
  376.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  377.     = get-input-buffer(stream);
  378.   // Make sure we have input, if there is any.
  379.   if (next == stop)
  380.     stop := fill-input-buffer(stream, 0);
  381.     next := 0;
  382.   end;
  383.   case
  384.     (stop ~= 0) =>
  385.       // We definitely have some input available.
  386.       block (exit-loop)
  387.     let res = "";
  388.     let collect = method (string :: <byte-string>, buf :: <buffer>,
  389.                   start :: <buffer-index>, stop :: <buffer-index>)
  390.             let str-len = string.size;
  391.             let buf-len = (stop - start);
  392.             let res = make(<byte-string>,
  393.                        size: (str-len + buf-len));
  394.             copy-bytes(res, 0, string, 0, str-len);
  395.             copy-bytes(res, str-len, buf, start, buf-len);
  396.             res;
  397.               end;
  398.     while (#t)
  399.       for (i from next below stop,
  400.            until (buf[i] = $newline-byte))
  401.       finally
  402.         if (i = stop)
  403.           res := collect(res, buf, next, stop);
  404.         else
  405.           res := collect(res, buf, next, i);
  406.           // We don't return the newline, but we do consume it.
  407.           release-input-buffer(stream, (i + 1), stop);
  408.           exit-loop(res, #f);
  409.         end;
  410.       end;
  411.       next := 0;
  412.       stop := fill-input-buffer(stream, 0);
  413.       if (stop = 0)
  414.         release-input-buffer(stream, 0, 0);
  415.         exit-loop(res, #t);
  416.       end;
  417.     end while;
  418.       end block;
  419.     (signal-eof?) =>
  420.       // Hit EOF immediately.
  421.       release-input-buffer(stream, 0, 0);
  422.       error(make(<end-of-file>, stream: stream));
  423.     otherwise =>
  424.       // Hit EOF immediately.
  425.       release-input-buffer(stream, 0, 0);
  426.       #f;
  427.   end case;
  428. end method;
  429.  
  430.  
  431. define generic input-available? (stream :: <stream>) => result :: <boolean>;
  432.  
  433. define method input-available? (stream :: <stream>) => result :: <boolean>;
  434.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  435.     = get-input-buffer(stream);
  436.   if (next == stop)
  437.     let res = input-available-at-source?(stream);
  438.     release-input-buffer(stream, 0, 0);
  439.     res;
  440.   else
  441.     release-input-buffer(stream, next, stop);
  442.     #t
  443.   end;
  444. end method;
  445.  
  446.  
  447. define generic flush-input (stream :: <stream>) => meaningless :: singleton(#f);
  448.  
  449. define method flush-input (stream :: <stream>) => meaningless :: singleton(#f);
  450.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  451.     = get-input-buffer(stream);
  452.   for (until (fill-input-buffer(stream, 0) = 0))
  453.   end;
  454.   release-input-buffer(stream, 0, 0);
  455.   #f;
  456. end method;
  457.  
  458.  
  459. define generic force-output (stream :: <stream>)
  460.     => meaningless :: singleton(#f);
  461.  
  462. define method force-output (stream :: <stream>)
  463.     => meaningless :: singleton(#f);
  464.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  465.     = get-output-buffer(stream);
  466.   if (next ~= 0) force-output-buffer(stream, next) end;
  467.   release-output-buffer(stream, 0);
  468.   #f;
  469. end method;
  470.  
  471.  
  472. define generic synchronize-output (stream :: <stream>)
  473.     => meaningless :: singleton(#f);
  474.  
  475. define method synchronize-output (stream :: <stream>)
  476.     => meaningless :: singleton(#f);
  477.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  478.     = get-output-buffer(stream);
  479.   if (next ~= 0) synchronize-output-buffer(stream, next) end;
  480.   release-output-buffer(stream, 0);
  481.   #f;
  482. end method;
  483.  
  484.  
  485.  
  486. //// Buffer Access Protocol.
  487. ////
  488.  
  489. /// All of these are exported.
  490. ///
  491. /// This page contains the generic function declarations as well as a default
  492. /// implementation for <stream>s.
  493. ///
  494.  
  495. /// get-input-buffer -- Exported.
  496. ///
  497. define generic get-input-buffer (stream :: <stream>)
  498.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  499.  
  500. define method get-input-buffer (stream :: <stream>)
  501.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  502.   // Isolate the calling thread's access to the stream.
  503.   lock-stream(stream);
  504.   // Make sure the thread does not already hold the buffer.
  505.   if (stream.buffer-locked?)
  506.     error("Application already holds stream's buffer -- %=.", stream);
  507.   else
  508.     stream.buffer-locked? := #t;
  509.   end;
  510.   stream-extension-get-input-buffer(stream);
  511. end method;
  512.  
  513. /// release-input-buffer -- Exported.
  514. ///
  515. define generic release-input-buffer
  516.     (stream :: <stream>, next :: <buffer-index>, stop :: <buffer-index>)
  517.     => meaningless :: singleton(#f);
  518.  
  519. define method release-input-buffer
  520.     (stream :: <stream>, next :: <buffer-index>, stop :: <buffer-index>)
  521.     => meaningless :: singleton(#f);
  522.   // Lock the stream to isolate checking whether the buffer is locked.
  523.   lock-stream(stream);
  524.   if (~ stream.buffer-locked?)
  525.     unlock-stream(stream);
  526.     error("Application does not hold stream's buffer -- %=.", stream);
  527.   end;
  528.   // Unlock the lock for checking buffer-locked?.
  529.   unlock-stream(stream);
  530.   // Because the buffer was locked, and we were able to obtain a lock, the
  531.   // calling thread must already hold a lock on the stream due to
  532.   // get-input-buffer.  Therefore, the rest of this code is still thread-safe.
  533.   stream-extension-release-input-buffer(stream, next, stop);
  534.   stream.buffer-locked? := #f;
  535.   // Unlock the lock obtained in get-input-buffer.
  536.   unlock-stream(stream);
  537. end method;
  538.  
  539. /// get-output-buffer -- Exported.
  540. ///
  541. define generic get-output-buffer (stream :: <stream>)
  542.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  543.  
  544. define method get-output-buffer (stream :: <stream>)
  545.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  546.   // Isolate the calling thread's access to the stream.
  547.   lock-stream(stream);
  548.   // Make sure the thread does not already hold the buffer.
  549.   if (stream.buffer-locked?)
  550.     error("Application already holds stream's buffer -- %=.", stream);
  551.   else
  552.     stream.buffer-locked? := #t;
  553.   end;
  554.   stream-extension-get-output-buffer(stream);
  555. end method;
  556.  
  557. /// release-output-buffer -- Exported.
  558. ///
  559. define generic release-output-buffer
  560.     (stream :: <stream>, next :: <buffer-index>)
  561.     => meaningless :: singleton(#f);
  562.  
  563. define method release-output-buffer (stream :: <stream>, next :: <buffer-index>)
  564.     => meaningless :: singleton(#f);
  565.   // Lock the stream to isolate checking whether the buffer is locked.
  566.   lock-stream(stream);
  567.   if (~ stream.buffer-locked?)
  568.     unlock-stream(stream);
  569.     error("Application does not hold stream's buffer -- %=.", stream);
  570.   end;
  571.   // Unlock the lock for checking buffer-locked?.
  572.   unlock-stream(stream);
  573.   // Because the buffer was locked, and we were able to obtain a lock, the
  574.   // calling thread must already hold a lock on the stream due to
  575.   // get-output-buffer.  Therefore, the rest of this code is still
  576.   // thread-safe.
  577.   stream-extension-release-output-buffer(stream, next);
  578.   stream.buffer-locked? := #f;
  579.   // Unlock the lock obtained in get-input-buffer.
  580.   unlock-stream(stream);
  581. end method;
  582.  
  583.  
  584.  
  585. //// Data Extension Protocol.
  586. ////
  587.  
  588. /// read-as -- Exported.
  589. ///
  590. define generic read-as (result-class :: <class>, stream :: <stream>,
  591.             #key signal-eof?: :: <boolean>) // = #t
  592.     => (result :: union(<object>, singleton(#f)), eof? :: <boolean>);
  593.  
  594.  
  595. define sealed method read-as
  596.     (result-class :: singleton(<byte-character>), stream :: <stream>,
  597.      #key signal-eof? :: <boolean> = #t)
  598.     => (result :: union(<byte-character>, singleton(#f)),
  599.     eof? :: <boolean>);
  600.   let res :: union(<byte>, singleton(#f))
  601.     = read-byte(stream, signal-eof?: signal-eof?);
  602.   // If read-byte returns, we either have a byte or signal-eof? was #f.
  603.   if (res)
  604.     values(as(<byte-character>, res), #f)
  605.   else
  606.     values(#f, #t);
  607.   end;
  608. end method;
  609.  
  610. define sealed method read-as
  611.     (result-class :: singleton(<byte>), stream :: <stream>,
  612.      #key signal-eof? :: <boolean> = #t)
  613.     => (result :: union(<byte>, singleton(#f)), eof? :: <boolean>);
  614.   let res :: union(<byte>, singleton(#f))
  615.     = read-byte(stream, signal-eof?: signal-eof?);
  616.   values(res, if (res) #f else #t end);
  617. end method;
  618.  
  619.  
  620.  
  621. /// read-as for <byte-string> and <byte-vector> results from <stream>s.
  622. ///
  623. /// Read-as for <byte-string> and <byte-vector> have the same definition.
  624. /// There are two "define method" forms so that the distinct return types
  625. /// can be distinctly declared.  If the "seal generic" form allowed you to
  626. /// declare return types, there could be one method here with two "seal
  627. /// generic" forms declaring the distinct specializations and their
  628. /// associated return types.
  629. ///
  630.  
  631. define method read-as
  632.     (result-class :: singleton(<byte-string>), stream :: <stream>,
  633.      #key signal-eof? :: <boolean> = #t,
  634.           count :: union(<fixed-integer>, singleton(#f)),
  635.           to-eof? :: <boolean> = #f)
  636.     => (result :: union(<byte-string>, singleton(#f)),
  637.     eof? :: <boolean>);
  638.   case
  639.     (count) => read-as-required-vector-count(stream, result-class, signal-eof?,
  640.                          count);
  641.     (to-eof?) => read-as-required-vector-to-eof(stream, result-class);
  642.     otherwise =>
  643.       error("Count or to-eof? must be supplied to read a <byte-string>.");
  644.   end;
  645. end method;
  646.  
  647. define method read-as
  648.     (result-class :: singleton(<byte-vector>), stream :: <stream>,
  649.      #key signal-eof? :: <boolean> = #t,
  650.           count :: union(<fixed-integer>, singleton(#f)),
  651.           to-eof? :: <boolean> = #f)
  652.     => (result :: union(<byte-vector>, singleton(#f)),
  653.     eof? :: <boolean>);
  654.   case
  655.     (count) => read-as-required-vector-count(stream, result-class, signal-eof?,
  656.                          count);
  657.     (to-eof?) => read-as-required-vector-to-eof(stream, result-class);
  658.     otherwise =>
  659.       error("Count or to-eof? must be supplied to read a <byte-vector>.");
  660.   end;
  661. end method;
  662.  
  663. /// read-as-required-vector-count -- Internal.
  664. ///
  665. /// This function implements read-as for <byte-string> and <byte-vector> for
  666. /// any stream when the user supplied a count: argument.  This function
  667. /// works for <buffer>s too due to the use of copy-bytes, but reading
  668. /// buffers is implemented for each stream type to avoid double buffering.
  669. ///
  670. define method read-as-required-vector-count
  671.     (stream :: <stream>,
  672.      result-class :: one-of(<byte-vector>, <byte-string>),
  673.      signal-eof? :: <boolean>,
  674.      count :: <fixed-integer>)
  675.     => (result :: type-or(<byte-vector>, <byte-string>, singleton(#f)),
  676.     eof? :: <boolean>);
  677.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  678.     = get-input-buffer(stream);
  679.   let result = make(result-class, size: count);
  680.   if (next == stop)
  681.     stop := fill-input-buffer(stream, 0);
  682.     next := 0;
  683.   end;
  684.   block (exit-loop)
  685.     let available :: <buffer-index> = (stop - next);
  686.     let result-start :: <fixed-integer> = 0;
  687.     let buf-start :: <buffer-index> = next;
  688.     for (until (available = 0))
  689.       let result-stop :: <fixed-integer> = (result-start + available);
  690.       if (result-stop >= count)
  691.     let this-copy = (count - result-start);
  692.     copy-bytes(result, result-start, buf, buf-start, this-copy);
  693.     release-input-buffer(stream, (buf-start + this-copy),
  694.                  // Can't assume buf-start is 0 because we may
  695.                  // come in here on the first iteration.
  696.                  (buf-start + available));
  697.     exit-loop(result, #f);
  698.       else
  699.     copy-bytes(result, result-start, buf, buf-start, available);
  700.       end;
  701.       available := fill-input-buffer(stream, 0);
  702.       result-start := result-stop;
  703.       buf-start := 0;
  704.     finally
  705.       // Whenever the loop terminates normally, we don't have enough input
  706.       // to satisfy the request.
  707.       release-input-buffer(stream, 0, 0);
  708.       if (signal-eof?)
  709.     error(make(<end-of-file>, stream: stream));
  710.       else
  711.     values(#f, #t);
  712.       end;
  713.     end for;
  714.   end block;
  715. end method;
  716.  
  717. /// read-as-required-vector-to-eof -- Internal.
  718. ///
  719. /// This function implements read-as for <byte-string>, <byte-vector>, and
  720. /// <buffer> for any stream when the user supplied a to-eof?: argument.
  721. /// There are better methods for <random-access-stream> and
  722. /// <fd-file-stream>.  If the users can't know the size of the stream, are
  723. /// using read-as to read to-eof, and asking for a <buffer> result, then
  724. /// they may as well get poor performance :-); seriously, they should be
  725. /// using the buffer directly.  The scenario described is a pretty unlikely
  726. /// one too.
  727. ///
  728. /// This function cannot assume the Random Access Protocol, so it must
  729. /// repeatedly fill the buffer and build intermediate results to satisfy the
  730. /// read request.  This function cannot use a big global buffer to build the
  731. /// result because of thread-safety.
  732. ///
  733. define method read-as-required-vector-to-eof
  734.     (stream :: <stream>,
  735.      result-class :: one-of(<byte-vector>, <byte-string>, <buffer>))
  736.     => (result :: type-or(<byte-vector>, <byte-string>, <buffer>),
  737.     eof? :: singleton(#t));
  738.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  739.     = get-input-buffer(stream);
  740.   // Make sure we have input if there is any.
  741.   if (next == stop)
  742.     stop := fill-input-buffer(stream, 0);
  743.     next := 0;
  744.   end;
  745.   let res = make(result-class, size: 0);
  746.   let res-len = 0;
  747.   for (next = next then 0,
  748.        stop = stop then fill-input-buffer(stream, 0),
  749.        until (stop = 0))
  750.     let buf-len = (stop - next);
  751.     let temp-len = (res-len + buf-len);
  752.     let temp = make(<byte-string>, size: temp-len);
  753.     copy-bytes(temp, 0, res, 0, res-len);
  754.     copy-bytes(temp, res-len, buf, next, buf-len);
  755.     res := temp;
  756.     res-len := temp-len;
  757.   finally
  758.     release-input-buffer(stream, 0, 0);
  759.     values(res, #t);
  760.   end for;
  761. end method;
  762.  
  763.  
  764.  
  765. /// read-as for <byte-string> and <byte-vector> results from <random-access-stream>s
  766. ///
  767. /// Read-as for <byte-string> and <byte-vector> have the same definition.
  768. /// There are two "define method" forms so that the distinct return types
  769. /// can be distinctly declared.  If the "seal generic" form allowed you to
  770. /// declare return types, there could be one method here with two "seal
  771. /// generic" forms declaring the distinct specializations and their
  772. /// associated return types.
  773. ///
  774.  
  775. define method read-as
  776.     (result-class :: singleton(<byte-string>), stream :: <random-access-stream>,
  777.      #key signal-eof? :: <boolean> = #t,
  778.           count :: union(<fixed-integer>, singleton(#f)),
  779.           to-eof? :: <boolean> = #f)
  780.     => (result :: union(<byte-string>, singleton(#f)),
  781.     eof? :: <boolean>);
  782.   case
  783.     (count) => read-as-required-vector-count(stream, result-class, signal-eof?,
  784.                          count);
  785.     (to-eof?) =>
  786.       // Isolate thread access across this call so that no thread intervenes
  787.       // between the calls to stream-size, stream-position, and read-as-r....
  788.       lock-stream(stream);
  789.       let res = read-as-required-vector-count(stream, result-class, #f,
  790.                           (stream.stream-size
  791.                          - stream.stream-position));
  792.       unlock-stream(stream);
  793.       values(res, #t);
  794.     otherwise =>
  795.       error("Count or to-eof? must be supplied to read a <byte-string>.");
  796.   end;
  797. end method;
  798.  
  799. define method read-as
  800.     (result-class :: singleton(<byte-vector>), stream :: <random-access-stream>,
  801.      #key signal-eof? :: <boolean> = #t,
  802.           count :: union(<fixed-integer>, singleton(#f)),
  803.           to-eof? :: <boolean> = #f)
  804.     => (result :: union(<byte-vector>, singleton(#f)),
  805.     eof? :: <boolean>);
  806.   case
  807.     (count) => read-as-required-vector-count(stream, result-class, signal-eof?,
  808.                          count);
  809.     (to-eof?) =>
  810.       // Isolate thread access across this call so that no thread intervenes
  811.       // between the calls to stream-size, stream-position, and read-as-r....
  812.       lock-stream(stream);
  813.       let res = read-as-required-vector-count(stream, result-class, #f,
  814.                           (stream.stream-size
  815.                          - stream.stream-position));
  816.       unlock-stream(stream);
  817.       values(res, #t);
  818.     otherwise =>
  819.       error("Count or to-eof? must be supplied to read a <byte-vector>.");
  820.   end;
  821. end method;
  822.  
  823.  
  824.  
  825. /// read-as for <buffer> results from <fd-stream>s.
  826. ///
  827. /// Read-as needs to be implemented for each stream class to avoid double
  828. /// buffering.
  829. ///
  830.  
  831. define method read-as
  832.     (result-class :: singleton(<buffer>), stream :: <fd-stream>,
  833.      #key signal-eof? :: <boolean> = #t,
  834.           count :: union(<fixed-integer>, singleton(#f)),
  835.           to-eof? :: <boolean> = #f)
  836.     => (result :: union(<buffer>, singleton(#f)), eof? :: <boolean>);
  837.   case
  838.     (count) => read-as-buffer-count(stream, result-class, signal-eof?, count);
  839.     (to-eof?) => read-as-required-vector-to-eof(stream, result-class);
  840.     otherwise =>
  841.       error("Count or to-eof? must be supplied to read a buffer.");
  842.   end;
  843. end method;
  844.  
  845. define method read-as-buffer-count
  846.     (stream :: <fd-stream>, result-class :: singleton(<buffer>),
  847.      signal-eof? :: <boolean>, count :: <fixed-integer>)
  848.     => (result :: union(<buffer>, singleton(#f)), eof? :: <boolean>);
  849.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  850.     = get-input-buffer(stream);
  851.   let result = make(<buffer>, size: count);
  852.   let available :: <buffer-index> = (stop - next);
  853.   if (available >= count)
  854.     // All the input we need is already available in the stream's buffer.
  855.     copy-bytes(result, 0, buf, next, count);
  856.     release-input-buffer(stream, next, stop);
  857.     values(result, #f);
  858.   else
  859.     // We need to iterate to get all the input we need.
  860.     // First, copy what is available in the stream's buffer to the result.
  861.     let start = if (available ~= 0)
  862.           copy-bytes(result, 0, buf, next, available);
  863.           available;
  864.         else
  865.           0;
  866.         end;
  867.     let fd = stream.file-descriptor;
  868.     block (exit-loop)
  869.       // Iterate, filling the result buffer directly.
  870.       for (num-bytes :: <buffer-index>
  871.          = call-fd-function(fd-read, fd, result, start, (count - start))
  872.          then call-fd-function(fd-read, fd, result, start,
  873.                    (count - start)),
  874.        until (num-bytes = 0))
  875.     start := start + num-bytes;
  876.     if (start = count)
  877.       release-input-buffer(stream, 0, 0);
  878.       exit-loop(result, #f);
  879.     end;
  880.       finally
  881.     // If we exit normally, then we hit eof.
  882.     release-input-buffer(stream, 0, 0);
  883.     if (signal-eof?)
  884.       error(make(<end-of-file>, stream: stream));
  885.     else
  886.       values(#f, #t);
  887.     end;
  888.       end for;
  889.     end block;
  890.   end if;
  891. end method;
  892.  
  893.  
  894.  
  895. /// read-as for <buffer> results from <fd-file-stream>s.
  896. ///
  897. /// Read-as needs to be implemented for each stream class to avoid double
  898. /// buffering.
  899. ///
  900. /// This method needs to exist even though there is a similar method on
  901. /// <random-access-stream>s because of how applicable methods are sorted.
  902. /// We need to make sure this method executes rather than the one for
  903. /// <fd-stream>s.
  904. ///
  905.  
  906. define method read-as
  907.     (result-class :: singleton(<buffer>), stream :: <fd-file-stream>,
  908.      #key signal-eof? :: <boolean> = #t,
  909.           count :: union(<fixed-integer>, singleton(#f)),
  910.           to-eof? :: <boolean> = #f)
  911.     => (result :: union(<buffer>, singleton(#f)), eof? :: <boolean>);
  912.   case
  913.     (count) =>
  914.       read-as-buffer-count(stream, result-class, signal-eof?, count);
  915.     (to-eof?) =>
  916.       // Isolate thread access across this call so that no thread intervenes
  917.       // between the calls to stream-size, stream-position, and read-as-b....
  918.       lock-stream(stream);
  919.       let res = read-as-buffer-count(stream, result-class, #f,
  920.                      (stream.stream-size
  921.                     - stream.stream-position));
  922.       unlock-stream(stream);
  923.       values(res, #t);
  924.     otherwise =>
  925.       error("Count or to-eof? must be supplied to read a buffer.");
  926.   end;
  927. end method;
  928.  
  929.  
  930.  
  931. /// read-as for <buffer> results from <byte-string-input-stream>s.
  932. ///
  933. /// Read-as needs to be implemented for each stream class to avoid double
  934. /// buffering.
  935. ///
  936.  
  937. define sealed method read-as
  938.     (result-class :: singleton(<buffer>), stream :: <byte-string-input-stream>,
  939.      #key signal-eof? :: <boolean> = #t,
  940.           count :: union(<fixed-integer>, singleton(#f)),
  941.           to-eof? :: <boolean> = #f)
  942.     => (result :: union(<buffer>, singleton(#f)), eof? :: <boolean>);
  943.   case
  944.     (count) => read-as-buffer-count(stream, result-class, signal-eof?, count);
  945.     (to-eof?) =>
  946.       // Isolate thread access across this call so that no thread intervenes
  947.       // between the calls to stream-size, stream-position, and read-as-b....
  948.       lock-stream(stream);
  949.       let res = read-as-buffer-count(stream, result-class, #f,
  950.                      (stream.stream-size
  951.                     - stream.stream-position));
  952.       unlock-stream(stream);
  953.       values(res, #t);
  954.     otherwise =>
  955.       error("Count or to-eof? must be supplied to read a buffer.");
  956.   end;
  957. end method;
  958.  
  959. define sealed method read-as-buffer-count
  960.     (stream :: <byte-string-input-stream>, result-class :: singleton(<buffer>),
  961.      signal-eof? :: <boolean>, count :: <fixed-integer>)
  962.     => (result :: union(<buffer>, singleton(#f)), eof? :: <boolean>);
  963.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  964.     = get-input-buffer(stream);
  965.   let available :: <buffer-index> = (stop - next);
  966.   if (available >= count)
  967.     let result = make(result-class, size: count);
  968.     copy-bytes(result, 0, buf, next, count);
  969.     release-input-buffer(stream, next, stop);
  970.     values(result, #f);
  971.   else
  972.     release-input-buffer(stream, 0, 0);
  973.     if (signal-eof?)
  974.       error(make(<end-of-file>, stream: stream));
  975.     else
  976.       values(#f, #t);
  977.     end;
  978.   end;
  979. end method;
  980.  
  981.  
  982.  
  983. /// read-into! for <byte-string>, <byte-vector>, and <buffer> results from <stream>s.
  984. ///
  985.  
  986. define generic read-into!
  987.     (destination :: <object>, stream :: <stream>,
  988.      #key signal-eof?: :: <boolean>, // = #t
  989.           start: :: <fixed-integer>, // = 0,
  990.           end: :: <fixed-integer>, // = destination.size,
  991.           to-eof?: :: <boolean>) // = #f)
  992.     => (result :: union(<object>, singleton(#f)),
  993.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  994.  
  995. /// Read-into! for <byte-string>, <byte-vector>, and <buffer> have the same
  996. /// definition.  There are three "define method" forms so that the distinct
  997. /// return types can be distinctly declared.  If the "seal generic" form
  998. /// allowed you to declare return types, there could be one method here
  999. /// with two "seal generic" forms declaring the distinct specializations
  1000. /// and their associated return types.
  1001. ///
  1002.  
  1003. define sealed method read-into!
  1004.     (destination :: <byte-string>, stream :: <stream>,
  1005.      #key signal-eof? :: <boolean> = #t,
  1006.           start :: <fixed-integer> = 0,
  1007.           end: stop :: <fixed-integer> = destination.size,
  1008.           to-eof? :: <boolean> = #f)
  1009.     => (result :: union(<byte-string>, singleton(#f)),
  1010.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1011.   read-into-required-vector(stream, destination, signal-eof?, to-eof?,
  1012.                 start, stop);
  1013. end method;
  1014.  
  1015. define sealed method read-into!
  1016.     (destination :: <byte-vector>, stream :: <stream>,
  1017.      #key signal-eof? :: <boolean> = #t,
  1018.           start :: <fixed-integer> = 0,
  1019.           end: stop :: <fixed-integer> = destination.size,
  1020.           to-eof? :: <boolean> = #f)
  1021.     => (result :: union(<byte-vector>, singleton(#f)),
  1022.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1023.   read-into-required-vector(stream, destination, signal-eof?, to-eof?,
  1024.                 start, stop);
  1025. end method;
  1026.  
  1027. define sealed method read-into!
  1028.     (destination :: <buffer>, stream :: <stream>,
  1029.      #key signal-eof? :: <boolean> = #t,
  1030.           start :: <fixed-integer> = 0,
  1031.           end: stop :: <fixed-integer> = destination.size,
  1032.           to-eof? :: <boolean> = #f)
  1033.     => (result :: union(<buffer>, singleton(#f)),
  1034.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1035.   read-into-required-vector(stream, destination, signal-eof?, to-eof?,
  1036.                 start, stop);
  1037. end method;
  1038.  
  1039. /// read-into-required-vector -- Internal.
  1040. ///
  1041. /// This function implements read-into! for <byte-string>, <byte-vector>,
  1042. /// and <buffer> for any stream.  There are better methods for <buffer>s on
  1043. /// <fd-stream>s and <byte-string-input-stream>s.
  1044. ///
  1045. define sealed method read-into-required-vector
  1046.     (stream :: <stream>,
  1047.      destination :: type-or(<byte-vector>, <byte-string>, <buffer>),
  1048.      signal-eof? :: <boolean>,
  1049.      to-eof? :: <boolean>,
  1050.      start :: <fixed-integer>,
  1051.      stop :: <fixed-integer>)
  1052.     => (result :: type-or(<byte-vector>, <byte-string>, <buffer>,
  1053.               singleton(#f)),
  1054.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1055.   let (buf :: <buffer>, buf-start :: <buffer-index>, buf-stop :: <buffer-index>)
  1056.     = get-input-buffer(stream);
  1057.   if (buf-start = buf-stop)
  1058.     buf-stop := fill-input-buffer(stream, 0);
  1059.     buf-start := 0;
  1060.   end;
  1061.   block (exit-loop)
  1062.     let available :: <buffer-index> = (buf-stop - buf-start);
  1063.     let count :: <fixed-integer> = available;
  1064.     let stop :: <fixed-integer> = if (to-eof?) destination.size else stop end;
  1065.     let dst-start :: <fixed-integer> = start;
  1066.     for (until (available = 0))
  1067.       let dst-stop :: <fixed-integer> = (dst-start + available);
  1068.       if (dst-stop >= stop)
  1069.     if (to-eof?)
  1070.       error("Destination not big enough to read to EOF -- %=.",
  1071.         destination);
  1072.     end;
  1073.     let this-copy = (stop - dst-start);
  1074.     copy-bytes(destination, dst-start, buf, buf-start, this-copy);
  1075.     release-input-buffer(stream, (buf-start + this-copy),
  1076.                  // Can't assume buf-start is 0 because we may
  1077.                  // come in here on the first iteration.
  1078.                  (buf-start + available));
  1079.     exit-loop(destination, #f);
  1080.       else
  1081.     copy-bytes(destination, dst-start, buf, buf-start, available);
  1082.       end;
  1083.       available := fill-input-buffer(stream, 0);
  1084.       count := (count + available);
  1085.       dst-start := dst-stop;
  1086.       buf-start := 0;
  1087.     finally
  1088.       // Whenever the loop terminates normally, we either successfully read
  1089.       // to EOF, or we failed to read the required data to fill destination
  1090.       // to stop.
  1091.       release-input-buffer(stream, 0, 0);
  1092.       case
  1093.     (to-eof?) => values(destination, (start + count));
  1094.     (signal-eof?) => error(make(<end-of-file>, stream: stream));
  1095.     otherwise => values(#f, #t);
  1096.       end;
  1097.     end for;
  1098.   end block;
  1099. end method;
  1100.  
  1101.  
  1102.  
  1103. /// read-into! for <buffer> destinations on <fd-stream>s and <byte-string-input-stream>s.
  1104. ///
  1105. /// This page contains read-into! methods that fill <buffer>s for
  1106. /// <fd-stream>s and <byte-string-input-stream>s.  Read-into! for <buffer>s
  1107. /// needs to be implemented for each stream class to avoid double
  1108. /// buffering.
  1109. ///
  1110.  
  1111. define sealed method read-into!
  1112.     (destination :: <buffer>, stream :: <fd-stream>,
  1113.      #key signal-eof? :: <boolean> = #t,
  1114.           start :: <buffer-index> = 0,
  1115.           end: stop :: <buffer-index> = destination.size,
  1116.           to-eof? :: <boolean> = #f)
  1117.     => (result :: union(<buffer>, singleton(#f)),
  1118.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1119.   let (buf :: <buffer>, buf-start :: <buffer-index>, buf-stop :: <buffer-index>)
  1120.     = get-input-buffer(stream);
  1121.   let (start :: <buffer-index>, count :: <buffer-index>)
  1122.     = if (buf-start = buf-stop)
  1123.     values(start, 0);
  1124.       else
  1125.     let count = min((buf-stop - buf-start), (stop - start));
  1126.     copy-bytes(destination, start, buf, buf-start, count);
  1127.     values((start + count), count);
  1128.       end;
  1129.   let fd = stream.file-descriptor;
  1130.   block (exit-loop)
  1131.     let stop :: <buffer-index> = if (to-eof?) destination.size else stop end;
  1132.     for (num-bytes :: <buffer-index>
  1133.        = call-fd-function(fd-read, fd, destination, start, (stop - start))
  1134.      then call-fd-function(fd-read, fd, destination, start,
  1135.                    (stop - start)),
  1136.      until (num-bytes = 0))
  1137.       start := start + num-bytes;
  1138.       count := count + num-bytes;
  1139.       case
  1140.     (start ~= stop) =>
  1141.       // Keep going and try to get more input.
  1142.       #f;   // Case is broken in Mindy.
  1143.     (~ to-eof?) =>
  1144.       // We got all the requested input, and we are not trying to read to
  1145.       // EOF.  Just return everything.
  1146.       release-input-buffer(stream, 0, 0);
  1147.       exit-loop(destination, #f);
  1148.     (call-fd-function(fd-read, fd, buf, 0, buf.size) ~= 0) =>
  1149.       // We're trying to read to EOF, and we've read everything the buffer
  1150.       // can hold.  Furthermore, there is still input available, so error.
  1151.       error("Destination not big enough to read to EOF -- %=.",
  1152.         destination);
  1153.     otherwise =>
  1154.       // Everything's cool.  Return successfully.
  1155.       release-input-buffer(stream, 0, 0);
  1156.       exit-loop(destination, count);
  1157.       end;
  1158.     finally
  1159.       // Whenever the loop terminates normally, we either successfully read
  1160.       // to EOF, or we failed to read the required data to fill the
  1161.       // destination to stop.
  1162.       release-input-buffer(stream, 0, 0);
  1163.       case
  1164.     (to-eof?) => values(destination, count);
  1165.     (signal-eof?) => error(make(<end-of-file>, stream: stream));
  1166.     otherwise => values(#f, #t);
  1167.       end;
  1168.     end for;
  1169.   end block;
  1170. end method;
  1171.  
  1172. define sealed method read-into!
  1173.     (destination :: <buffer>, stream :: <byte-string-input-stream>,
  1174.      #key signal-eof? :: <boolean> = #t,
  1175.           start :: <buffer-index> = 0,
  1176.           end: stop :: <buffer-index> = destination.size,
  1177.           to-eof? :: <boolean>)
  1178.     => (result :: union(<buffer>, singleton(#f)),
  1179.     eof?-or-how-much :: union(<boolean>, <integer>));
  1180.   let (buf :: <buffer>, next :: <buffer-index>, buf-stop :: <buffer-index>)
  1181.     = get-input-buffer(stream);
  1182.   let available :: <buffer-index> = (buf-stop - next);
  1183.   if (to-eof?)
  1184.     if (available <= (destination.size - start))
  1185.       copy-bytes(destination, start, buf, next, available);
  1186.       values(destination, available);
  1187.     else
  1188.       release-input-buffer(stream, next, buf-stop);
  1189.       error("Destination not big enough to read to EOF -- %=.",
  1190.         destination);
  1191.     end;
  1192.   else
  1193.     let need :: <buffer-index> = (stop - start);
  1194.     if (available >= need)
  1195.       copy-bytes(destination, 0, buf, next, need);
  1196.       release-input-buffer(stream, next, buf-stop);
  1197.       values(destination, #f);
  1198.     else
  1199.       release-input-buffer(stream, 0, 0);
  1200.       if (signal-eof?)
  1201.     error(make(<end-of-file>, stream: stream));
  1202.       else
  1203.     values(#f, #t);
  1204.       end;
  1205.     end;
  1206.   end;
  1207. end method;
  1208.  
  1209.  
  1210.  
  1211. /// write
  1212. ///
  1213.  
  1214. define generic write (object :: <object>, stream :: <stream>, #key)
  1215.     => stream :: <stream>;
  1216.  
  1217.  
  1218. define sealed method write (object :: <byte-character>, stream :: <stream>,
  1219.                 #key)
  1220.     => stream :: <stream>;
  1221.   let (buf :: <buffer>, next :: <buffer-index>, size :: <buffer-index>)
  1222.     = get-output-buffer(stream);
  1223.   if (next = size)
  1224.     force-output-buffer(stream, size);
  1225.     next := 0;
  1226.   end;
  1227.   buf[next] := as(<byte>, object);
  1228.   release-output-buffer(stream, next + 1);
  1229.   stream;
  1230. end method;
  1231.  
  1232. define sealed method write (object :: <byte>, stream :: <stream>, #key)
  1233.     => stream :: <stream>;
  1234.   let (buf :: <buffer>, next :: <buffer-index>, size :: <buffer-index>)
  1235.     = get-output-buffer(stream);
  1236.   if (next = size)
  1237.     force-output-buffer(stream, size);
  1238.     next := 0;
  1239.   end;
  1240.   buf[next] := object;
  1241.   release-output-buffer(stream, next + 1);
  1242.   stream;
  1243. end method;
  1244.  
  1245. /// This method implements the write function for <byte-string> and
  1246. /// <byte-vector>.  This function would work for <buffer>s too, but writing
  1247. /// buffers is implemented for each stream individually to avoid double
  1248. /// buffer.
  1249. ///
  1250. define sealed method write (object :: type-or(<byte-vector>, <byte-string>),
  1251.                 stream :: <stream>,
  1252.                 #key start :: <fixed-integer> = 0,
  1253.                      end: stop :: <fixed-integer> = object.size)
  1254.     => stream :: <stream>;
  1255.   let (buf :: <buffer>, next :: <buffer-index>, size :: <buffer-index>)
  1256.     = get-output-buffer(stream);
  1257.   if (next = size)
  1258.     force-output-buffer(stream, size);
  1259.     next := 0;
  1260.   end;
  1261.   block (exit-loop)
  1262.     let buf-capacity :: <buffer-index> = (size - next);
  1263.     let buf-start :: <buffer-index> = next;
  1264.     while (#t)
  1265.       let partial-stop :: <fixed-integer> = (start + buf-capacity);
  1266.       if (partial-stop >= stop)
  1267.     let this-copy = (stop - start);
  1268.     copy-bytes(buf, buf-start, object, start, this-copy);
  1269.     release-output-buffer(stream, (buf-start + this-copy));
  1270.     exit-loop(stream);
  1271.       else
  1272.     copy-bytes(buf, buf-start, object, start, buf-capacity);
  1273.       end;
  1274.       force-output-buffer(stream, size);
  1275.       buf-capacity := size;
  1276.       buf-start := 0;
  1277.       start := partial-stop;
  1278.     end;
  1279.   end block;
  1280.   stream;
  1281. end method;
  1282.  
  1283. // Mindy does not parse "seal generic" forms currently.
  1284. // The streams spec requires sealed methods for these types.
  1285. //
  1286. // seal generic write (<byte-vector>, <stream>);
  1287. // seal generic write (<byte-string>, <stream>);
  1288. //
  1289.  
  1290.  
  1291.  
  1292. /// write for <buffer>s.
  1293. ///
  1294. /// This page contains implementations of write for each stream type so that
  1295. /// writing buffers can avoid double buffering.
  1296. ///
  1297.  
  1298. define sealed method write (object :: <buffer>, stream :: <fd-stream>,
  1299.                 #key start :: <fixed-integer> = 0,
  1300.                      end: stop :: <fixed-integer> = object.size)
  1301.     => stream :: <stream>;
  1302.   let (buf :: <buffer>, next :: <buffer-index>)
  1303.     = get-output-buffer(stream);
  1304.   if (next ~= 0)
  1305.     force-output-buffer(stream, next);
  1306.   end;
  1307.   let fd = stream.file-descriptor;
  1308.   let buf = stream.buffer;
  1309.   // Keep writing until fd-write manages to write everything.
  1310.   for (x :: <buffer-index>
  1311.      = (start + call-fd-function(fd-write, fd, object, start, stop))
  1312.          then (x + call-fd-function(fd-write, fd, buf, x, stop - x)),
  1313.        until (x = stop))
  1314.   end;
  1315.   release-output-buffer(stream, 0);
  1316.   stream;
  1317. end method;
  1318.  
  1319. define sealed method write
  1320.     (object :: <buffer>, stream :: <byte-string-output-stream>,
  1321.      #key start :: <fixed-integer> = 0,
  1322.           end: stop :: <fixed-integer> = object.size)
  1323.     => stream :: <stream>;
  1324.   let (buf :: <buffer>, buf-stop :: <buffer-index>)
  1325.     = get-output-buffer(stream);
  1326.   let object-len :: <fixed-integer> = (stop - start);
  1327.   let backup :: union(<byte-string>, singleton(#f))
  1328.               = stream.string-output-stream-backup;
  1329.   if (backup)
  1330.     // Collect all output into a new backup.
  1331.     let backup-len :: <fixed-integer> = backup.size;
  1332.     let new-backup-len = backup-len + object-len + buf-stop;
  1333.     let new-backup :: <byte-string>
  1334.       = make(<byte-string>, size: new-backup-len);
  1335.     copy-bytes(new-backup, 0, backup, 0, backup-len);
  1336.     let backup-and-buf-len = (backup-len + buf-stop);
  1337.     if (buf-stop ~= 0)
  1338.       copy-bytes(new-backup, backup-len, buf, 0, buf-stop);
  1339.     end;
  1340.     copy-bytes(new-backup, backup-and-buf-len, object, start, object-len);
  1341.     stream.string-output-stream-backup := new-backup;
  1342.   else
  1343.     // Collect any output into a backup and leave the stream's buffer empty.
  1344.     let backup-len = object-len + buf-stop;
  1345.     let backup :: <byte-string>
  1346.       = make(<byte-string>, size: backup-len);
  1347.     if (buf-stop ~= 0)
  1348.       copy-bytes(backup, 0, buf, 0, buf-stop);
  1349.     end;
  1350.     copy-bytes(backup, buf-stop, object, start, object-len);
  1351.     stream.string-output-stream-backup := backup;
  1352.   end;
  1353.   release-output-buffer(stream, 0);
  1354.   stream;
  1355. end method;
  1356.  
  1357.  
  1358.  
  1359. /// write-line
  1360. ///
  1361.  
  1362. define generic write-line (object :: <object>, stream :: <stream>, #all-keys)
  1363.     => stream :: <stream>;
  1364.  
  1365.  
  1366. define method write-line (object :: <object>, stream :: <stream>,
  1367.               #rest key-args, #all-keys)
  1368.     => stream :: <stream>;
  1369.   lock-stream(stream);
  1370.   apply(write, object, stream, key-args);
  1371.   write('\n', stream);
  1372.   unlock-stream(stream);
  1373.   stream;
  1374. end method;
  1375.  
  1376.  
  1377.  
  1378. //// Fd Streams -- class definition and Stream Extension Protocol.
  1379. ///
  1380.  
  1381. /// <fd-stream> Class -- Exported.
  1382. ///
  1383. /// All file descriptor based streams inherit from this class.
  1384. ///
  1385. /// This is a non-standard class defined for Gwydion streams.  This stream
  1386. /// and <file-stream> are the superclasses of <fd-file-stream>s.
  1387. ///
  1388. define class <fd-stream> (<stream>)
  1389.   //
  1390.   // This slot holds the direction of the file-descriptor.  <fd-stream>s have
  1391.   // a single direction, as presented to the user.  However, if the file
  1392.   // descriptor really refers to a file, then the <fd-stream> is actually
  1393.   // bidirectional.  For <fd-stream>s, this slot is used to enforce the
  1394.   // direction specified when making the stream.  For <fd-file-stream>s,
  1395.   // this slot indicates the direction the user last used the stream, and the
  1396.   // value of this slot changes as the user changes directions of the
  1397.   // <fd-file-stream>.
  1398.   slot fd-direction :: one-of(#"input", #"output");
  1399.   slot file-descriptor :: <integer>;
  1400.   //
  1401.   // This slot has a buffer when the stream is open, #f when closed.
  1402.   slot buffer :: union(<buffer>, singleton(#f));
  1403.   //
  1404.   // Buffer-next for input: streams holds the next available byte for input.
  1405.   // For output: streams this slot holds the next available location for
  1406.   // placing output.
  1407.   slot buffer-next :: <buffer-index>;
  1408.   //
  1409.   // Buffer-stop for input: streams holds the end of the available input.
  1410.   // This slot holds no meaningful value for output: streams.
  1411.   slot buffer-stop :: <buffer-index>;
  1412. end class;
  1413.  
  1414. define sealed method close (stream :: <fd-stream>)
  1415.     => meaningless :: singleton(#f);
  1416.   if (stream.fd-direction == #"input")
  1417.     // Get buffer to make sure no one holds it.
  1418.     get-input-buffer(stream);
  1419.     call-fd-function(fd-close, stream.file-descriptor);
  1420.     stream.buffer := #f;
  1421.     release-input-buffer(stream, 0, 0);
  1422.     #f;
  1423.   else
  1424.     let (buf :: <buffer>, next :: <buffer-index>)
  1425.       =    get-output-buffer(stream);
  1426.     if (next ~= 0) synchronize-output-buffer(stream, next) end;
  1427.     call-fd-function(fd-close, stream.file-descriptor);
  1428.     stream.buffer := #f;
  1429.     unregister-output-stream(stream);
  1430.     release-output-buffer(stream, 0);
  1431.     #f;
  1432.   end;
  1433. end method;
  1434.   
  1435. define sealed method initialize
  1436.     (stream :: <fd-stream>, #next next-method,
  1437.      #key direction :: one-of(#"input", #"output") = #"input",
  1438.           fd :: <integer>,
  1439.           size: length :: <buffer-index> = $default-buffer-size)
  1440.     => result :: <fd-stream>;
  1441.   next-method();
  1442.   stream.fd-direction := direction;
  1443.   stream.file-descriptor := fd;
  1444.   stream.buffer := make(<buffer>, size: length);
  1445.   if (direction == #"input")
  1446.     // Next and stop are the same so that the first read will fill the buffer.
  1447.     stream.buffer-next := (stream.buffer-stop := 0);
  1448.   else
  1449.     register-output-stream(stream);
  1450.     stream.buffer-next := 0;
  1451.   end;
  1452.   stream;
  1453. end method;
  1454.  
  1455. define sealed method stream-extension-get-input-buffer
  1456.     (stream :: <fd-stream>)
  1457.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  1458.   let direction = stream.fd-direction;
  1459.   if (direction == #"output")
  1460.     error("Stream is an output stream -- %=.", stream);
  1461.   end;
  1462.   let buf = stream.buffer;
  1463.   // Since buffer is currently unheld by anyone, make sure it isn't closed.
  1464.   if (~ buf) error("Stream has been closed -- %=.", stream) end;
  1465.   values(buf, stream.buffer-next, stream.buffer-stop);
  1466. end method;
  1467.  
  1468. define sealed method stream-extension-release-input-buffer
  1469.     (stream :: <fd-stream>, next :: <buffer-index>, stop :: <buffer-index>)
  1470.     => meaningless :: singleton(#f);
  1471.   let direction = stream.fd-direction;
  1472.   case
  1473.     (direction == #"output") =>
  1474.       error("Stream is an output stream -- %=.", stream);
  1475.     (stop < next) =>
  1476.       error("Returned buffer with stop, %d, less than next, %d.", stop, next);
  1477.     otherwise =>
  1478.       stream.buffer-next := next;
  1479.       stream.buffer-stop := stop;
  1480.       #f;
  1481.   end;
  1482. end method;
  1483.  
  1484. define sealed method fill-input-buffer (stream :: <fd-stream>,
  1485.                     start :: <buffer-index>)
  1486.     => stop :: <buffer-index>;
  1487.   // Lock the stream to isolate checking whether the buffer is locked.
  1488.   lock-stream(stream);
  1489.   if (~ stream.buffer-locked?)
  1490.     unlock-stream(stream);
  1491.     error("Application does not hold stream's buffer -- %=.", stream);
  1492.   end;
  1493.   // Unlock the lock for checking buffer-locked?.
  1494.   unlock-stream(stream);
  1495.   // Because the buffer was locked, and we were able to obtain a lock, the
  1496.   // calling thread must already hold a lock on the stream due to
  1497.   // get-input-buffer.  Therefore, the rest of this code is still thread-safe.
  1498.   let direction = stream.fd-direction;
  1499.   if (direction == #"output")
  1500.     error("Stream is an output stream -- %=.", stream);
  1501.   end;
  1502.   let buf = stream.buffer;
  1503.   let count = call-fd-function(fd-read, stream.file-descriptor, buf,
  1504.                    start, (buf.size - start));
  1505.   // Don't bother updating stream's notion of next and stop because we
  1506.   // rely on what the users tell us when they return the buffer.  Just
  1507.   // return the value.
  1508.   if (count = 0)
  1509.     0;
  1510.   else
  1511.     start + count;
  1512.   end;
  1513. end method;
  1514.  
  1515. define sealed method input-available-at-source? (stream :: <fd-stream>)
  1516.     => available? :: <boolean>;
  1517.   // Lock the stream to isolate checking whether the buffer is locked.
  1518.   lock-stream(stream);
  1519.   if (~ stream.buffer-locked?)
  1520.     unlock-stream(stream);
  1521.     error("Application does not hold stream's buffer -- %=.", stream);
  1522.   end;
  1523.   // Unlock the lock for checking buffer-locked?.
  1524.   unlock-stream(stream);
  1525.   // Because the buffer was locked, and we were able to obtain a lock, the
  1526.   // calling thread must already hold a lock on the stream due to
  1527.   // get-input-buffer.  Therefore, the rest of this code is still thread-safe.
  1528.   let direction = stream.fd-direction;
  1529.   if (direction == #"output")
  1530.     error("Stream is an output stream -- %=.", stream);
  1531.   end;
  1532.   call-fd-function(fd-input-available?, stream.file-descriptor);
  1533. end method;
  1534.  
  1535. define sealed method stream-extension-get-output-buffer
  1536.     (stream :: <fd-stream>)
  1537.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  1538.   let direction = stream.fd-direction;
  1539.   if (direction == #"input")
  1540.     error("Stream is an input stream -- %=.", stream);
  1541.   end;
  1542.   let buf = stream.buffer;
  1543.   // Since no one holds the buffer, make sure the stream isn't closed.
  1544.   unless (buf) error("Stream has been closed -- %=.", stream); end;
  1545.   let next :: <buffer-index> = stream.buffer-next;
  1546.   let buf-size :: <buffer-index> = buf.size;
  1547.   if (next = buf-size)
  1548.     let fd = stream.file-descriptor;
  1549.     // Keep writing until fd-write manages to write everything.
  1550.     for (x :: <buffer-index>
  1551.         = call-fd-function(fd-write, fd, buf, 0, next)
  1552.         then (x + call-fd-function(fd-write, fd, buf, x, next - x)),
  1553.      until (x = next))
  1554.     end;
  1555.     values(buf, 0, buf-size)
  1556.   else
  1557.     values(buf, next, buf-size);
  1558.   end;
  1559. end method;
  1560.  
  1561. define sealed method stream-extension-release-output-buffer
  1562.     (stream :: <fd-stream>, next :: <buffer-index>)
  1563.     => meaningless :: singleton(#f);
  1564.   let direction = stream.fd-direction;
  1565.   if (direction == #"input")
  1566.     error("Stream is an input stream -- %=.", stream);
  1567.   end;
  1568.   stream.buffer-next := next;
  1569.   #f;
  1570. end method;
  1571.  
  1572. define sealed method force-output-buffer (stream :: <fd-stream>,
  1573.                       stop :: <buffer-index>)
  1574.     => meaningless :: singleton(#f);
  1575.   // Lock the stream to isolate checking whether the buffer is locked.
  1576.   lock-stream(stream);
  1577.   if (~ stream.buffer-locked?)
  1578.     unlock-stream(stream);
  1579.     error("Application does not hold stream's buffer -- %=.", stream);
  1580.   end;
  1581.   // Unlock the lock for checking buffer-locked?.
  1582.   unlock-stream(stream);
  1583.   // Because the buffer was locked, and we were able to obtain a lock, the
  1584.   // calling thread must already hold a lock on the stream due to
  1585.   // get-output-buffer.  Therefore, the rest of this code is still
  1586.   // thread-safe.
  1587.   if (stream.fd-direction == #"input")
  1588.     error("Stream is an input stream -- %=.", stream);
  1589.   end;
  1590.   let fd = stream.file-descriptor;
  1591.   let buf = stream.buffer;
  1592.   // Keep writing until fd-write manages to write everything.
  1593.   for (x :: <buffer-index> = call-fd-function(fd-write, fd, buf, 0, stop)
  1594.          then (x + call-fd-function(fd-write, fd, buf, x, stop - x)),
  1595.        until (x = stop))
  1596.   end;
  1597. end;
  1598.  
  1599. define sealed method synchronize-output-buffer
  1600.     (stream :: <fd-stream>, stop :: <buffer-index>)
  1601.     => meaningless :: singleton(#f);
  1602.   force-output-buffer(stream, stop);
  1603.   call-fd-function(fd-sync-output, stream.file-descriptor);
  1604.   #f;
  1605. end;
  1606.  
  1607.  
  1608.  
  1609. //// Random Access Streams --  generic function declarations.
  1610. ////
  1611.  
  1612. /// All of these are exported.
  1613. ///
  1614.  
  1615. define generic stream-position (stream :: <random-access-stream>)
  1616.     => position :: <integer>;
  1617.  
  1618. define generic stream-position-setter
  1619.     (position :: <integer>, stream :: <random-access-stream>)
  1620.     => position :: <integer>;
  1621.  
  1622. define generic adjust-stream-position
  1623.     (offset :: <integer>,
  1624.      stream :: <random-access-stream>,
  1625.      #key from: :: one-of(#"start", #"current", #"end")) // = #"start"
  1626.     => position :: <integer>;
  1627.  
  1628. define generic stream-size (stream :: <random-access-stream>)
  1629.     => size :: <integer>;
  1630.  
  1631.  
  1632.  
  1633. //// Fd File Streams -- class declarations and Random Access Protocol.
  1634. ////
  1635.  
  1636. /// <file-stream> Class -- Exported.
  1637. ///
  1638. define abstract class <file-stream> (<random-access-stream>)
  1639. end class;
  1640.  
  1641. /// <fd-file-stream> Class -- Internal.
  1642. ///
  1643. /// This is the concrete class that is instantiated when users make a
  1644. /// <file-stream>.
  1645. ///
  1646. define sealed class <fd-file-stream> (<fd-stream>, <file-stream>)
  1647.   slot file-name :: <byte-string>;
  1648.   slot file-direction :: one-of(#"input", #"output", #"input-output");
  1649. end class;
  1650.  
  1651.  
  1652. /// stream-position -- Method for Exported Interface.
  1653. ///
  1654. define sealed method stream-position (stream :: <fd-file-stream>)
  1655.     => position :: <integer>;
  1656.   if (stream.file-direction == #"input")
  1657.     // Get the buffer to ensure no one else is using it and to make it
  1658.     // possible to correctly compute the actual file position.
  1659.     let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  1660.       = get-input-buffer(stream);
  1661.     ignore(buf);
  1662.     // Get the current position as recorded by the file-descritor module
  1663.     // and subtract what input we have in the buffer but haven't actually
  1664.     // read.
  1665.     let pos = (fd-seek(stream.file-descriptor, 0, fd-seek-current)
  1666.          - (stop - next));
  1667.     release-input-buffer(stream, next, stop);
  1668.     pos;
  1669.   else
  1670.     // Direction is #"output" or #"input-output".
  1671.     // Get the buffer to ensure no one else is using it and to make it
  1672.     // possible to correctly compute the actual file position.
  1673.     let (buf, next :: <buffer-index>, stop)
  1674.       = get-output-buffer(stream);
  1675.     ignore(buf, stop);
  1676.     // Get the current position as recorded by the file-descritor module
  1677.     // and add what output we have in the buffer but haven't sent yet.
  1678.     let pos = fd-seek(stream.file-descriptor, 0, fd-seek-current) + next;
  1679.     release-output-buffer(stream, next);
  1680.     pos;
  1681.   end;
  1682. end method;
  1683.  
  1684. /// stream-position-setter -- Method for Exported Interface.
  1685. ///
  1686. define sealed method stream-position-setter
  1687.     (position :: <integer>, stream :: <fd-file-stream>)
  1688.     => position :: <integer>;
  1689.   let direction = file-direction(stream);
  1690.   // Get the buffer to ensure no one else is using it and to make it
  1691.   // possible to invalidate the buffer's contents.
  1692.   if (direction == #"input")
  1693.     get-input-buffer(stream);
  1694.   else
  1695.     let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  1696.       = get-output-buffer(stream);
  1697.     ignore(buf, stop);
  1698.     // Force any pending output so that we can later correctly test for the
  1699.     // file's size.
  1700.     if (next > 0)
  1701.       force-output-buffer(stream, next);
  1702.     end;
  1703.   end;
  1704.   // Set the position.
  1705.   let fd = stream.file-descriptor;
  1706.   if ((position > 0) &
  1707.       (position < call-fd-function(fd-seek, fd, 0, fd-seek-end)))
  1708.     call-fd-function(fd-seek, fd, position, fd-seek-start);
  1709.   else
  1710.     error("Illegal stream position -- %d", position);
  1711.   end;
  1712.   // Cleanup.
  1713.   if (direction == #"input")
  1714.     release-input-buffer(stream, 0, 0);
  1715.   else
  1716.     release-output-buffer(stream, 0);
  1717.   end;
  1718.   position;
  1719. end method;
  1720.  
  1721. /// adjust-stream-position -- Method for Exported Interface.
  1722. ///
  1723. define sealed method adjust-stream-position
  1724.     (offset :: <integer>, stream :: <fd-file-stream>, 
  1725.      #key from: reference :: one-of(#"start", #"current", #"end") = #"start")
  1726.     => position :: <integer>;
  1727.   let direction = file-direction(stream);
  1728.   if (direction == #"input")
  1729.     // Get the buffer to ensure no one else is using it and to make it
  1730.     // possible to invalidate the buffer's contents.
  1731.     let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  1732.       = get-input-buffer(stream);
  1733.     ignore(buf);
  1734.     if (reference == #"current")
  1735.       // If moving the position relative to the current position, then
  1736.       // adjust the offset to account for the unread input in the buffer.
  1737.       // Because of the unread input, the file-descriptor module's record
  1738.       // of the position is ahead of the actual position.
  1739.       offset := offset - (stop - next);
  1740.     end;
  1741.     let pos = call-fd-function(fd-seek, stream.file-descriptor, offset,
  1742.                    select (reference)
  1743.                  (#"start") => fd-seek-start;
  1744.                  (#"current") => fd-seek-current;
  1745.                  (#"end") => fd-seek-end;
  1746.                    end);
  1747.     release-input-buffer(stream, 0, 0);
  1748.     pos;
  1749.   else
  1750.     // Get the buffer to ensure no one else is using it and to make it
  1751.     // possible to invalidate the buffer's contents.
  1752.     let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  1753.       = get-output-buffer(stream);
  1754.     ignore(buf, stop);
  1755.     // Force out any pending output while the file position is still right
  1756.     // for the file to receive this output.
  1757.     if (next > 0)
  1758.       force-output-buffer(stream, next);
  1759.     end;
  1760.     let pos = call-fd-function(fd-seek, stream.file-descriptor, offset,
  1761.                    select (reference)
  1762.                  (#"start") => fd-seek-start;
  1763.                  (#"current") => fd-seek-current;
  1764.                  (#"end") => fd-seek-end;
  1765.                    end);
  1766.     release-output-buffer(stream, 0);
  1767.     pos;
  1768.   end;
  1769. end method;
  1770.  
  1771. /// stream-size -- Method for Exported Interface.
  1772. ///
  1773. define sealed method stream-size (stream :: <fd-file-stream>)
  1774.     => size :: <integer>;
  1775.   if (stream.file-direction == #"input")
  1776.     // Get the buffer to ensure no one else is using it and to make it
  1777.     // possible to correctly compute the actual file position.
  1778.     let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  1779.       = get-input-buffer(stream);
  1780.     ignore(buf);
  1781.     let pos = fd-seek(stream.file-descriptor, 0, fd-seek-current);
  1782.     let size = fd-seek(stream.file-descriptor, 0, fd-seek-end);
  1783.     fd-seek(stream.file-descriptor, pos, fd-seek-start);
  1784.     release-input-buffer(stream, next, stop);
  1785.     size;
  1786.   else
  1787.     // Direction is #"output" or #"input-output".
  1788.     // Get the buffer to ensure no one else is using it and to make it
  1789.     // possible to correctly compute the actual file position and size.
  1790.     let (buf, next :: <buffer-index>, stop)
  1791.       = get-output-buffer(stream);
  1792.     ignore(buf, stop);
  1793.     // Force any pending output so that we can later correctly test for the
  1794.     // file's size.  We don't know if the current pending output is
  1795.     // overwriting part of the file or extending its length.
  1796.     if (next > 0)
  1797.       force-output-buffer(stream, next);
  1798.     end;
  1799.     let pos = fd-seek(stream.file-descriptor, 0, fd-seek-current);
  1800.     let size = fd-seek(stream.file-descriptor, 0, fd-seek-end);
  1801.     fd-seek(stream.file-descriptor, pos, fd-seek-start);
  1802.     release-output-buffer(stream, next);
  1803.     size;
  1804.   end;
  1805. end method;
  1806.  
  1807.  
  1808.  
  1809. //// Fd File Streams -- Stream Extension Protocol.
  1810. ////
  1811.  
  1812. /// The following methods from <fd-streams> work:
  1813. ///    close
  1814. ///    synchronize-output-buffer
  1815. ///
  1816.  
  1817.  
  1818. /// file-buffer-last-use -- Internal.
  1819. /// file-buffer-last-use-setter -- Internal.
  1820. ///
  1821. /// These are defined for readability.
  1822. ///
  1823. define constant file-buffer-last-use = fd-direction;
  1824. define constant file-buffer-last-use-setter = fd-direction-setter;
  1825.  
  1826.  
  1827. define method make (result-class :: singleton(<file-stream>), #rest keys,
  1828.             #all-keys)
  1829.     => result :: <fd-file-stream>;
  1830.   apply(make, <fd-file-stream>, keys);
  1831. end method;
  1832.  
  1833. define sealed method initialize
  1834.     (stream :: <fd-file-stream>, #next next-method, #rest rest-args,
  1835.      #key name :: union(<byte-string>, singleton(#f)),
  1836.           direction :: one-of(#"input", #"output", #"input-output")
  1837.                      = #"input",
  1838.           if-exists :: one-of(#"signal", #"replace", #"overwrite",
  1839.                   #"append")
  1840.                  = #"replace",
  1841.       size: length :: <buffer-index> = $default-buffer-size)
  1842.     => result :: <fd-file-stream>;
  1843.   if (~ name)
  1844.     error("Must supply a filename when making a <file-stream>.");
  1845.   end;
  1846.   if (direction == #"input")
  1847.     let (fd, err) = fd-open(name, fd-o_rdonly);
  1848.     case
  1849.       (~ err) => #f;   // Case is broken in Mindy.
  1850.       (err = fd-enoent) => error(make(<file-not-found>, filename: name));
  1851.       // Do not pass error string directly because it might have something
  1852.       // that looks like a control-string directive.
  1853.       otherwise => error("%S", fd-error-string(err))
  1854.     end;
  1855.     stream.file-name := name;
  1856.     stream.file-direction := #"input";
  1857.     apply(next-method, stream, fd: fd, direction: #"input", rest-args); 
  1858.     stream;
  1859.   else
  1860.     // Make an #"output" or #"input-output" stream.
  1861.     let flags :: <integer> = fd-o_creat;
  1862.     flags := select (direction)
  1863.            (#"output") => logior(flags, fd-o_wronly);
  1864.            (#"input-output") => logior(flags, fd-o_rdwr);
  1865.          end;
  1866.     flags := select (if-exists)
  1867.            (#"signal") => logior(flags, fd-o_excl);
  1868.            (#"replace") => logior(flags, fd-o_trunc);
  1869.            otherwise => flags;
  1870.          end;
  1871.     let (fd, err) = fd-open(name, flags);
  1872.     case
  1873.       (~ err) => #f;   // Case is broken in Mindy.
  1874.       (err = fd-eexist) => error(make(<file-exists>, filename: name));
  1875.       // Do not pass error string directly because it might have something
  1876.       // that looks like a control-string directive.
  1877.       otherwise => error("%S", fd-error-string(err))
  1878.     end;
  1879.     if (if-exists == #"append")
  1880.       call-fd-function(fd-seek, fd, 0, fd-seek-end);
  1881.     end;
  1882.     stream.file-name := name;
  1883.     stream.file-direction := direction;
  1884.     apply(next-method, stream, fd: fd,
  1885.       direction: if (direction == #"output") #"output" else #"input" end,
  1886.       rest-args); 
  1887.     register-output-stream(stream);
  1888.   end;
  1889. end method;
  1890.  
  1891. define sealed method close (stream :: <fd-file-stream>, #next next-method)
  1892.     => meaningless :: singleton(#f);
  1893.   next-method();
  1894.   if ((stream.file-direction == #"input-output")
  1895.     & (stream.file-buffer-last-use == #"input"))
  1896.     unregister-output-stream(stream);
  1897.   end;
  1898.   #f;
  1899. end method;
  1900.  
  1901. /// This method does not call next-method because this method does most of the
  1902. /// work determining what to do, and if it did call next-method, in one case
  1903. /// it would have to do extra work just to make next-method work.
  1904. ///
  1905. /// This method does not have to check whether the stream or buffer is locked
  1906. /// because get-input-buffer does that.
  1907. /// 
  1908. define sealed method stream-extension-get-input-buffer
  1909.     (stream :: <fd-file-stream>)
  1910.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  1911.   let direction = stream.file-direction;
  1912.   if (direction == #"output")
  1913.     error("Stream is an output stream -- %=.", stream);
  1914.   end;
  1915.   let buf = stream.buffer;
  1916.   // Since buffer is currently unheld by anyone, make sure it isn't closed.
  1917.   unless (buf) error("Stream has been closed -- %=.", stream); end;
  1918.   if ((direction == #"input") | (stream.file-buffer-last-use == #"input"))
  1919.     values(buf, stream.buffer-next, stream.buffer-stop);
  1920.   else
  1921.     // The stream is both #"input-output" and was last used for #"output".
  1922.     let next :: <buffer-index> = stream.buffer-next;
  1923.     if (next > 0)
  1924.       // Keep writing until fd-write manages to write everything.
  1925.       let fd = stream.file-descriptor;
  1926.       for (x :: <buffer-index>
  1927.          = call-fd-function(fd-write, fd, buf, 0, next)
  1928.          then (x + call-fd-function(fd-write, fd, buf, x, next - x)),
  1929.        until (x = next))
  1930.       end;
  1931.     end;
  1932.     stream.file-buffer-last-use := #"input";
  1933.     // There's no reason to update the stream's notion of next and stop
  1934.     // because we rely on the users' values when they return the buffer.
  1935.     values(buf, 0, 0);
  1936.   end;
  1937. end method;
  1938.  
  1939. /// This method does not call next-method because this method does most of the
  1940. /// work determining what to do, and then only sets two slots.
  1941. ///
  1942. /// This method does not have to check whether the stream or buffer is locked
  1943. /// because release-input-buffer does that.
  1944. ///
  1945. define sealed method stream-extension-release-input-buffer
  1946.     (stream :: <fd-file-stream>, next :: <buffer-index>, stop :: <buffer-index>)
  1947.     => meaningless :: singleton(#f);
  1948.   let direction = stream.file-direction;
  1949.   case (direction == #"output") =>
  1950.       error("Stream is an output stream -- %=.", stream);
  1951.     (~ ((direction == #"input") |
  1952.     (stream.file-buffer-last-use == #"input"))) =>
  1953.       error("Buffer is currently held for output -- %=.", stream);
  1954.     (stop < next) =>
  1955.       error("Returned buffer with stop, %d, less than next, %d.", stop, next);
  1956.     otherwise =>
  1957.       stream.buffer-next := next;
  1958.       stream.buffer-stop := stop;
  1959.       #f;
  1960.   end;
  1961. end method;
  1962.  
  1963. /// This method does not call next-method because it would waste time doing
  1964. /// some tests again and then only execute a few statements.
  1965. ///
  1966. define sealed method fill-input-buffer (stream :: <fd-file-stream>,
  1967.                     start :: <buffer-index>)
  1968.     => stop :: <buffer-index>;
  1969.   // Lock the stream to isolate checking whether the buffer is locked.
  1970.   lock-stream(stream);
  1971.   if (~ stream.buffer-locked?)
  1972.     unlock-stream(stream);
  1973.     error("Application does not hold stream's buffer -- %=.", stream);
  1974.   end;
  1975.   // Unlock the lock for checking buffer-locked?.
  1976.   unlock-stream(stream);
  1977.   // Because the buffer was locked, and we were able to obtain a lock, the
  1978.   // calling thread must already hold a lock on the stream due to
  1979.   // get-input-buffer.  Therefore, the rest of this code is still thread-safe.
  1980.   let direction = stream.file-direction;
  1981.   if (direction == #"output")
  1982.     error("Stream is an output stream -- %=.", stream);
  1983.   end;
  1984.   if ((direction == #"input") | (stream.file-buffer-last-use == #"input"))
  1985.     let buf = stream.buffer;
  1986.     let count = call-fd-function(fd-read, stream.file-descriptor, buf,
  1987.                  start, (buf.size - start));
  1988.     // Don't bother updating stream's notion of next and stop because we
  1989.     // rely on what the users tell us when they return the buffer.  Just
  1990.     // return the value.
  1991.     if (count = 0)
  1992.       0;
  1993.     else
  1994.       start + count;
  1995.     end;
  1996.   else
  1997.     error("Buffer is currently held for output -- %=.", stream);
  1998.   end;
  1999. end method;
  2000.  
  2001. /// This method does not call next-method because it would waste time doing
  2002. /// some tests again and then only execute one line.
  2003. ///
  2004. define sealed method input-available-at-source? (stream :: <fd-file-stream>)
  2005.     => available? :: <boolean>;
  2006.   // Lock the stream to isolate checking whether the buffer is locked.
  2007.   lock-stream(stream);
  2008.   if (~ stream.buffer-locked?)
  2009.     unlock-stream(stream);
  2010.     error("Application does not hold stream's buffer -- %=.", stream);
  2011.   end;
  2012.   // Unlock the lock for checking buffer-locked?.
  2013.   unlock-stream(stream);
  2014.   // Because the buffer was locked, and we were able to obtain a lock, the
  2015.   // calling thread must already hold a lock on the stream due to
  2016.   // get-input-buffer.  Therefore, the rest of this code is still thread-safe.
  2017.   let direction = stream.file-direction;
  2018.   if (direction == #"output")
  2019.     error("Stream is an output stream -- %=.", stream);
  2020.   end;
  2021.   if ((direction == #"input") | (stream.file-buffer-last-use == #"input"))
  2022.     call-fd-function(fd-input-available?, stream.file-descriptor);
  2023.   else
  2024.     error("Buffer is currently held for output -- %=.", stream);
  2025.   end;
  2026. end method;
  2027.  
  2028. /// This method does not call next-method because this method does most of the
  2029. /// work determining what to do, and if it did call next-method, in one case
  2030. /// it would have to do extra work just to make next-method work.
  2031. /// 
  2032. /// This method does not have to check whether the stream or buffer is locked
  2033. /// because get-output-buffer does that.
  2034. ///
  2035. define sealed method stream-extension-get-output-buffer
  2036.     (stream :: <fd-file-stream>)
  2037.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  2038.   let direction = stream.file-direction;
  2039.   if (direction == #"input")
  2040.     error("Stream is an input stream -- %=.", stream);
  2041.   end;
  2042.   let buf = stream.buffer;
  2043.   // Since buffer is unheld by anyone, make sure it isn't closed.
  2044.   unless (buf) error("Stream has been closed -- %=.", stream); end;
  2045.   let next :: <buffer-index> = stream.buffer-next;
  2046.   let buf-size :: <buffer-index> = buf.size;
  2047.   if ((direction == #"output") | (stream.file-buffer-last-use == #"output"))
  2048.     if (next = buf-size)
  2049.       let fd = stream.file-descriptor;
  2050.       // Keep writing until fd-write manages to write everything.
  2051.       for (x :: <buffer-index>
  2052.          = call-fd-function(fd-write, fd, buf, 0, next)
  2053.          then (x + call-fd-function(fd-write, fd, buf, x, next - x)),
  2054.        until (x = next))
  2055.       end;
  2056.       values(buf, 0, buf-size)
  2057.     else
  2058.       values(buf, next, buf-size);
  2059.     end;
  2060.   else
  2061.     // The stream is both #"input-output" and was last used for #"input".
  2062.     let stop :: <buffer-index> = stream.buffer-stop;
  2063.     if (stop > next)
  2064.       // Set the file position correctly relative to the actual reading done
  2065.       // on the stream so that when users force output, it goes to the right
  2066.       // location in the file.
  2067.       call-fd-function(fd-seek, stream.file-descriptor, next - stop,
  2068.                fd-seek-current);
  2069.     end;
  2070.     stream.file-buffer-last-use := #"output";
  2071.     values(buf, 0, buf-size);
  2072.   end;
  2073. end method;
  2074.  
  2075. /// This method does not call next-method because this method does most of the
  2076. /// work determining what to do, and then only sets a slot.
  2077. ///
  2078. /// This method does not have to check whether the stream or buffer is locked
  2079. /// because release-output-buffer does that.
  2080. ///
  2081. define sealed method stream-extension-release-output-buffer
  2082.     (stream :: <fd-file-stream>, next :: <buffer-index>)
  2083.     => meaningless :: singleton(#f);
  2084.   let direction = stream.file-direction;
  2085.   if (direction == #"input")
  2086.     error("Stream is an input stream -- %=.", stream);
  2087.   end;
  2088.   if ((direction == #"output") | (stream.file-buffer-last-use == #"output"))
  2089.     stream.buffer-next := next;
  2090.     #f;
  2091.   else
  2092.     error("Buffer is currently held for input -- %=.", stream);
  2093.   end;
  2094. end method;
  2095.  
  2096. /// This method does not call next-method because it would waste time doing
  2097. /// some tests again and then only execute a few statements.
  2098. ///
  2099. define sealed method force-output-buffer (stream :: <fd-file-stream>,
  2100.                       stop :: <buffer-index>)
  2101.     => meaningless :: singleton(#f);
  2102.   // Lock the stream to isolate checking whether the buffer is locked.
  2103.   lock-stream(stream);
  2104.   if (~ stream.buffer-locked?)
  2105.     unlock-stream(stream);
  2106.     error("Application does not hold stream's buffer -- %=.", stream);
  2107.   end;
  2108.   // Unlock the lock for checking buffer-locked?.
  2109.   unlock-stream(stream);
  2110.   // Because the buffer was locked, and we were able to obtain a lock, the
  2111.   // calling thread must already hold a lock on the stream due to
  2112.   // get-output-buffer.  Therefore, the rest of this code is still
  2113.   // thread-safe.
  2114.   if (stream.file-direction == #"input")
  2115.     error("Stream is an input stream -- %=.", stream);
  2116.   end;
  2117.   if ((stream.file-direction == #"input-output") &
  2118.       (stream.file-buffer-last-use == #"input"))
  2119.     error("Buffer last used for input -- %=.", stream);
  2120.   end;
  2121.   let fd = stream.file-descriptor;
  2122.   let buf = stream.buffer;
  2123.   // Keep writing until fd-write manages to write everything.
  2124.   for (x :: <buffer-index> = call-fd-function(fd-write, fd, buf, 0, stop)
  2125.          then (x + call-fd-function(fd-write, fd, buf, x, stop - x)),
  2126.        until (x = stop))
  2127.   end;
  2128. end;
  2129.  
  2130.  
  2131.  
  2132. //// String Input Streams -- Stream Extension Protocol.
  2133. ////
  2134.  
  2135. /// The <string-input-stream> class is the class from which all other
  2136. /// string-input streams inherit.  This class cannot define slots for
  2137. /// subclasses to inherit because the stream interface makes no provision
  2138. /// for implementors of new string-input streams to access whatever commonly
  2139. /// defined slots subclasses might have.
  2140. ///
  2141. define abstract class <string-input-stream> (<random-access-stream>)
  2142. end class;
  2143.  
  2144. define method make (result-class :: singleton(<string-input-stream>),
  2145.             #rest keys, #all-keys);
  2146.   error("<string-input-stream> is not instantiable.  In this implementation "
  2147.     "of streams, you should call make on <byte-string-input-stream>.");
  2148. end method;
  2149.  
  2150. define class <byte-string-input-stream> (<string-input-stream>)
  2151.   slot buffer :: union(<buffer>, singleton(#f));
  2152.   slot buffer-next :: <buffer-index>;
  2153.   slot buffer-stop :: <buffer-index>;
  2154. end class;
  2155.  
  2156. define sealed method initialize
  2157.     (stream :: <byte-string-input-stream>,
  2158.      #next next-method,
  2159.      #key string :: <byte-string> = "",
  2160.           start :: <fixed-integer> = 0,
  2161.           end: stop :: <fixed-integer> = string.size,
  2162.       size: length :: <buffer-index> = 0)
  2163.     => result :: <byte-string-input-stream>;
  2164.   ignore(length);
  2165.   // Do some bounds checking ...
  2166.   if (start < 0)
  2167.     error("Bounds error in string -- %d.", start);
  2168.   end;
  2169.   if (stop > string.size)
  2170.     error("Bounds error in string -- %d.", stop);
  2171.   end;
  2172.   if (start > stop)
  2173.     error("Start, %d, must be less than or equal to end, %d.", start, stop);
  2174.   end;
  2175.   next-method();
  2176.   // Fill in the stream's slots and copy the string into the buffer.
  2177.   let length :: <buffer-index> = stop - start;
  2178.   let buf :: <buffer> = make(<buffer>, size: length);
  2179.   stream.buffer := buf;
  2180.   copy-bytes(buf, 0, string, start, length);
  2181.   stream.buffer-next := 0;
  2182.   stream.buffer-stop := length;
  2183.   stream;
  2184. end method;
  2185.  
  2186. define sealed method close (stream :: <byte-string-input-stream>)
  2187.     => meaningless :: singleton(#f);
  2188.   // Get buffer to make sure no one else holds it.
  2189.   get-input-buffer(stream);
  2190.   stream.buffer := #f;
  2191.   release-input-buffer(stream, 0, 0);
  2192.   #f;
  2193. end method;
  2194.  
  2195. define sealed method stream-extension-get-input-buffer
  2196.     (stream :: <byte-string-input-stream>)
  2197.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  2198.   let buf = stream.buffer;
  2199.   // Since buffer is currently unheld by anyone, make sure it isn't closed.
  2200.   unless (buf) error("Stream has been closed -- %=.", stream); end;
  2201.   values(buf, stream.buffer-next, stream.buffer-stop);
  2202. end method;
  2203.  
  2204. define sealed method stream-extension-release-input-buffer
  2205.     (stream :: <byte-string-input-stream>, next :: <buffer-index>,
  2206.      stop :: <buffer-index>)
  2207.     => meaningless :: singleton(#f);
  2208.   if (stop < next)
  2209.     error("Returned buffer with stop, %d, less than next, %d.", stop, next);
  2210.   else
  2211.     stream.buffer-next := next;
  2212.     stream.buffer-stop := stop;
  2213.     #f;
  2214.   end;
  2215. end method;
  2216.  
  2217. define sealed method fill-input-buffer (stream :: <byte-string-input-stream>,
  2218.                     start :: <buffer-index>)
  2219.     => stop :: <buffer-index>;
  2220.   // Lock the stream to isolate checking whether the buffer is locked.
  2221.   lock-stream(stream);
  2222.   if (~ stream.buffer-locked?)
  2223.     unlock-stream(stream);
  2224.     error("Application does not hold stream's buffer -- %=.", stream);
  2225.   end;
  2226.   // Unlock the lock for checking buffer-locked?.
  2227.   unlock-stream(stream);
  2228.   // Because the buffer was locked, and we were able to obtain a lock, the
  2229.   // calling thread must already hold a lock on the stream due to
  2230.   // get-input-buffer.  Therefore, the rest of this code is still
  2231.   // thread-safe.
  2232.   //
  2233.   // You can never get more input for the buffer, so return zero.
  2234.   0;
  2235. end method;
  2236.  
  2237. define sealed method input-available-at-source?
  2238.     (stream :: <byte-string-input-stream>)
  2239.     => available? :: <boolean>;
  2240.   // Lock the stream to isolate checking whether the buffer is locked.
  2241.   lock-stream(stream);
  2242.   if (~ stream.buffer-locked?)
  2243.     unlock-stream(stream);
  2244.     error("Application does not hold stream's buffer -- %=.", stream);
  2245.   end;
  2246.   // Unlock the lock for checking buffer-locked?.
  2247.   unlock-stream(stream);
  2248.   // Because the buffer was locked, and we were able to obtain a lock, the
  2249.   // calling thread must already hold a lock on the stream due to
  2250.   // get-input-buffer.  Therefore, the rest of this code is still
  2251.   // thread-safe.
  2252.   //
  2253.   // You can never get more input for the buffer.
  2254.   #f;
  2255. end method;
  2256.  
  2257.  
  2258.  
  2259. //// String Input Streams -- Random Access Protocol.
  2260. ////
  2261.  
  2262. /// All of these methods are for exported functions.
  2263. ///
  2264.  
  2265. define sealed method stream-position (stream :: <byte-string-input-stream>)
  2266.     => position :: <integer>;
  2267.   // Get the buffer to ensure no one else is using it.
  2268.   let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  2269.     = get-input-buffer(stream);
  2270.   ignore(buf);
  2271.   release-input-buffer(stream, next, stop);
  2272.   next;
  2273. end method;
  2274.  
  2275. define sealed method stream-position-setter
  2276.     (position :: <integer>, stream :: <byte-string-input-stream>)
  2277.     => position :: <integer>;
  2278.   // Get the buffer to ensure no one else is using it.
  2279.   let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  2280.     = get-input-buffer(stream);
  2281.   ignore(buf, next);
  2282.   if ((position < 0) | (position > stop))
  2283.     error("Illegal stream position -- %d.", position);
  2284.   end;
  2285.   release-input-buffer(stream, position, stop);
  2286.   position;
  2287. end method;
  2288.  
  2289. /// This method does not call stream-position-setter because this method
  2290. /// does most of the work determining what to do, and then just releases
  2291. /// the buffer.
  2292. ///
  2293. define sealed method adjust-stream-position
  2294.     (offset :: <integer>,
  2295.      stream :: <byte-string-input-stream>,
  2296.      #key from: reference :: one-of(#"start", #"current", #"end") = #"start")
  2297.     => position :: <integer>;
  2298.   // Get the buffer to ensure no one else is using it.
  2299.   let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  2300.     = get-input-buffer(stream);
  2301.   ignore(buf);
  2302.   let position = select (reference)
  2303.            (#"start") => offset;
  2304.            (#"current") => (next + offset);
  2305.            (#"end") => (stop + offset);
  2306.          end;
  2307.   if ((position < 0) | (position > stop))
  2308.     error("Illegal stream position -- %d.", position);
  2309.   end;
  2310.   release-input-buffer(stream, position, stop);
  2311.   position;
  2312. end method;
  2313.  
  2314. define sealed method stream-size (stream :: <byte-string-input-stream>)
  2315.     => size :: <integer>;
  2316.   // Get the buffer to ensure no one else is using it.
  2317.   let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  2318.     = get-input-buffer(stream);
  2319.   ignore(buf);
  2320.   release-input-buffer(stream, next, stop);
  2321.   stop;
  2322. end method;
  2323.  
  2324.  
  2325.  
  2326. //// String Output Streams -- classes, protocol, and Stream Extension Protocol.
  2327. ////
  2328.  
  2329. /// The <string-output-stream> class is the class from which all other
  2330. /// string-output streams inherit.  This class cannot define slots for
  2331. /// subclasses to inherit because the stream interface makes no provision
  2332. /// for implementors of new string-output streams to access whatever commonly
  2333. /// defined slots subclasses might have.
  2334. ///
  2335. define abstract class <string-output-stream> (<random-access-stream>)
  2336. end class;
  2337.  
  2338. define method make (result-class :: singleton(<string-output-stream>),
  2339.             #rest keys, #all-keys);
  2340.   error("<string-output-stream> is not instantiable.  In this implementation "
  2341.     "of streams, you should call make on <byte-string-output-stream>.");
  2342. end method;
  2343.  
  2344. /// This class collects its output in a buffer.  This makes mutual exclusion
  2345. /// easier because internal code can use the Buffer Access Protocol.  Also,
  2346. /// because the sequence operations in Dylan are nearly worthless, internal
  2347. /// code can use the <buffer> protocol to copy stuff around.  This saves
  2348. /// writing our own string to string copying routines.
  2349. ///
  2350. define class <byte-string-output-stream> (<string-output-stream>)
  2351.   slot buffer :: union(<buffer>, singleton(#f));
  2352.   slot string-output-stream-backup :: union(<byte-string>, singleton(#f)),
  2353.     init-value: #f;
  2354.   //
  2355.   // This slot holds the current position for writing into the buffer.
  2356.   slot buffer-next :: <buffer-index>, init-value: 0;
  2357.   //
  2358.   // This slot holds the end of the output held in the buffer.  Because of the
  2359.   // Random Access Protocol buffer-next may not be at the end of all the output
  2360.   // written.
  2361.   slot buffer-stop :: <buffer-index>, init-value: 0;
  2362. end class;
  2363.  
  2364. /// This method does not call register-output-stream because it is
  2365. /// meaningless to force output on a <byte-string-output-stream> when the
  2366. /// application exits.
  2367. ///
  2368. define sealed method initialize
  2369.     (stream :: <byte-string-output-stream>,
  2370.      #next next-method,
  2371.      #key size: length :: <buffer-index> = $default-buffer-size)
  2372.     => result :: <byte-string-output-stream>;
  2373.   stream.buffer := make(<buffer>, size: length);
  2374.   stream;
  2375. end method;
  2376.  
  2377. /// string-output-stream-string -- Exported.
  2378. ///
  2379. define generic string-output-stream-string (stream :: <string-output-stream>)
  2380.     => output :: <string>;
  2381.  
  2382. /// string-output-stream-string -- Method for Exported Interface.
  2383. ///
  2384. /// Collect the output backed up in the stream as a <byte-string> and
  2385. /// the pending output in the stream's buffer, and return this as a
  2386. /// <byte-string>.
  2387. ///                        
  2388. define sealed method string-output-stream-string
  2389.     (stream :: <byte-string-output-stream>)
  2390.     => output :: <byte-string>;
  2391.   let buf :: <buffer> = get-output-buffer(stream);
  2392.   let backup :: union(<byte-string>, singleton(#f))
  2393.               = stream.string-output-stream-backup;
  2394.   let output-len :: <fixed-integer> = stream.buffer-stop;
  2395.   let string
  2396.     = case
  2397.     (~ backup) =>
  2398.       // The only output is what is in the buffer.
  2399.       let res = make(<byte-string>, size: output-len);
  2400.       copy-bytes(res, 0, buf, 0, output-len);
  2401.       res;
  2402.     (output-len = 0) =>
  2403.       // The only output is what is in the backup string.
  2404.       backup;
  2405.     otherwise =>
  2406.       // Get output from both the backup string and the buffer.
  2407.       let backup-len :: <fixed-integer> = backup.size;
  2408.       let res :: <byte-string>
  2409.         = make(<byte-string>, size: (backup-len + output-len));
  2410.       copy-bytes(res, 0, backup, 0, backup-len);
  2411.       copy-bytes(res, backup-len, buf, 0, output-len);
  2412.       res;
  2413.       end;
  2414.   stream.string-output-stream-backup := #f;
  2415.   stream.buffer-stop := 0;
  2416.   release-output-buffer(stream, 0);
  2417.   string;
  2418. end method;
  2419.  
  2420. /// close -- Method for Exported Interface.
  2421. ///
  2422. define sealed method close (stream :: <byte-string-output-stream>)
  2423.     => meaningless :: singleton(#f);
  2424.   // Get the buffer to make sure no one is using it.
  2425.   get-output-buffer(stream);
  2426.   stream.buffer := #f;
  2427.   unregister-output-stream(stream);
  2428.   release-output-buffer(stream, 0);
  2429.   #f;
  2430. end method;
  2431.  
  2432. /// stream-extension-get-output-buffer -- Method for Exported Interface.
  2433. ///
  2434. /// This must not return a full buffer.  When the buffer is full, this
  2435. /// creates a backup store using a <byte-string>.  If there is already a
  2436. /// backup string, then this function creates a new one to hold all the
  2437. /// previously backed up output and what is in the buffer.
  2438. ///
  2439. define sealed method stream-extension-get-output-buffer
  2440.     (stream :: <byte-string-output-stream>)
  2441.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  2442.   let buf :: <buffer> = stream.buffer;
  2443.   let buf-next :: <buffer-index> = stream.buffer-next;
  2444.   let buf-len :: <buffer-index> = buf.size;
  2445.   // Test buf-next rather that buffer-stop.  Though buffer-stop may indicate
  2446.   // the buffer is full, there's no reason to back up the buffer when the
  2447.   // buf-next says the user isn't writing off the end of the buffer.
  2448.   if (buf-next = buf-len)
  2449.     // Can't write further in the buffer.
  2450.     let backup :: union(<byte-string>, singleton(#f))
  2451.                 = stream.string-output-stream-backup;
  2452.     if (backup)
  2453.       // Concatenate the backup and buffer to form new backup string.
  2454.       let backup-len :: <fixed-integer> = backup.size;
  2455.       let new-backup-len = backup-len + buf-len;
  2456.       let res :: <byte-string>
  2457.     = make(<byte-string>, size: new-backup-len);
  2458.       copy-bytes(res, 0, backup, 0, backup-len);
  2459.       copy-bytes(res, backup-len, buf, 0, buf-len);
  2460.       stream.string-output-stream-backup := res;
  2461.     else
  2462.       // Just copy the buffer into a backup string.
  2463.       stream.string-output-stream-backup :=
  2464.         buffer-subsequence(buf, <byte-string>, 0, buf-len);
  2465.     end;
  2466.     // Make sure buffer-stop is maintained correctly, and we move any output
  2467.     // remaining in the buffer to the beginning of the buffer.  This ensure
  2468.     // the output is correctly placed to be overwritten.  We do not update
  2469.     // buffer-next since we must rely on the user's value when he releases
  2470.     // the buffer.
  2471.     let stop :: <buffer-index> = stream.buffer-stop;
  2472.     if (stop > buf-next)
  2473.       let new-stop :: <buffer-index> = (stop - buf-next);
  2474.       copy-bytes(buf, 0, buf, next, new-stop);
  2475.       stream.buffer-stop := new-stop;
  2476.     else
  2477.       stream.buffer-stop := 0;
  2478.     end;
  2479.     values(buf, 0, buf-len);
  2480.   else
  2481.     // Just return the values, nothing special to do.
  2482.     values(buf, buf-next, buf-len);
  2483.   end;
  2484. end method;
  2485.  
  2486. define sealed method stream-extension-release-output-buffer
  2487.     (stream :: <byte-string-output-stream>, next :: <buffer-index>)
  2488.     => meaningless :: singleton(#f);
  2489.   stream.buffer-next := next;
  2490.   if (next > stream.buffer-stop) stream.buffer-stop := next end;
  2491.   #f;
  2492. end method;
  2493.  
  2494. define sealed method force-output-buffer
  2495.     (stream :: <byte-string-output-stream>, stop :: <buffer-index>)
  2496.     => meaningless :: singleton(#f);
  2497.   let buf :: <buffer> = stream.buffer;
  2498.   let backup :: union(<byte-string>, singleton(#f))
  2499.               = stream.string-output-stream-backup;
  2500.   if (backup)
  2501.     // Add output in buffer to backup.
  2502.     let backup-len :: <fixed-integer> = backup.size;
  2503.     let new-backup-len = backup-len + stop;
  2504.     let res :: <byte-string>
  2505.       = make(<byte-string>, size: new-backup-len);
  2506.     copy-bytes(res, 0, backup, 0, backup-len);
  2507.     copy-bytes(res, backup-len, buf, 0, stop);
  2508.     stream.string-output-stream-backup := res;
  2509.   else
  2510.     // Just create a backup string.
  2511.     stream.string-output-stream-backup
  2512.       := buffer-subsequence(buf, <byte-string>, 0, stop);
  2513.   end;
  2514.   // Make sure buffer-stop is maintained correctly, and we move any left over
  2515.   // output to the beginning of the buffer to be overwritten.  We do not
  2516.   // update buffer-next since we must rely on the user's value when he releases
  2517.   // the buffer.
  2518.   let real-stop :: <buffer-index> = stream.buffer-stop;
  2519.   if (real-stop > stop)
  2520.     let new-stop :: <buffer-index> = (real-stop - stop);
  2521.     copy-bytes(buf, 0, buf, stop, new-stop);
  2522.     stream.buffer-stop := new-stop;
  2523.   else
  2524.     stream.buffer-stop := 0;
  2525.   end;
  2526.   #f;
  2527. end method;
  2528.  
  2529. define sealed method synchronize-output-buffer
  2530.     (stream :: <byte-string-output-stream>, stop :: <buffer-index>)
  2531.     => meaningless :: singleton(#f);
  2532.   force-output-buffer(stream, stop);
  2533. end method;
  2534.  
  2535.  
  2536.  
  2537. //// String output streams -- Random Access Protocol.
  2538. ////
  2539.  
  2540. /// All of these methods are for exported functions.
  2541. ///
  2542.  
  2543. define method stream-position (stream :: <byte-string-output-stream>)
  2544.     => position :: <integer>;
  2545.   // Get the output buffer to make sure the stream is not already in use.
  2546.   let (buf :: <buffer>, next :: <buffer-index>) = get-output-buffer(stream);
  2547.   ignore(buf);
  2548.   let backup :: union(<byte-string>, singleton(#f))
  2549.               = stream.string-output-stream-backup;
  2550.   release-output-buffer(stream, next);
  2551.   if (backup)
  2552.     backup.size + next;
  2553.   else
  2554.     next;
  2555.   end;
  2556. end method;
  2557.  
  2558. define method stream-position-setter (position :: <integer>,
  2559.                       stream :: <byte-string-output-stream>)
  2560.     => position :: <integer>;
  2561.   // Get the output buffer to make sure the stream is not already in use.
  2562.   let (buf :: <buffer>, next :: <buffer-index>) = get-output-buffer(stream);
  2563.   let stop :: <buffer-index> = stream.buffer-stop;
  2564.   let backup :: union(<byte-string>, singleton(#f))
  2565.               = stream.string-output-stream-backup;
  2566.   let backup-len :: <integer> = if (backup) backup.size else 0 end;
  2567.   let stream-len :: <integer> = backup-len + stop;
  2568.   if ((position < 0) | (position > stream-len))
  2569.     error("Illegal stream position -- %d.", position);
  2570.   end;
  2571.   if (position >= backup-len)
  2572.     // Reposition within the existing buffer.
  2573.     release-output-buffer(stream, (position - backup-len));
  2574.   else
  2575.     new-string-output-stream-backup(stream, buf, stop, backup, backup-len);
  2576.     release-output-buffer(stream, position);
  2577.   end;
  2578.   position;
  2579. end method;
  2580.  
  2581. /// This could be a literal constant in the following method definition, but
  2582. /// Dylan failed to incorporate any means for cleanly identifying non-printing
  2583. /// characters in character and string literals.  I don't want to use my
  2584. /// editor to quote non-printing characters into my program's source.
  2585. ///
  2586. define constant $null-char = as(<byte-character>, 0);
  2587.  
  2588. define method adjust-stream-position
  2589.     (offset :: <integer>,
  2590.      stream :: <byte-string-output-stream>,
  2591.      #key from: reference :: one-of(#"start", #"current", #"end") = #"start")
  2592.     => position :: <integer>;
  2593.   // Get the output buffer to make sure the stream is not already in use.
  2594.   let (buf :: <buffer>, buf-next :: <buffer-index>)
  2595.     = get-output-buffer(stream);
  2596.   let stop :: <buffer-index> = stream.buffer-stop;
  2597.   let backup :: union(<byte-string>, singleton(#f))
  2598.               = stream.string-output-stream-backup;
  2599.   let backup-len :: <integer> = if (backup) backup.size else 0 end;
  2600.   let stream-len :: <integer> = backup-len + stop;
  2601.   let position = select (reference)
  2602.            (#"start") => offset;
  2603.            (#"current") => (buf-next + offset);
  2604.            (#"end") => (stream-len + offset);
  2605.          end;
  2606.   case
  2607.     (position < 0) =>
  2608.       error("Illegal stream position -- %d.", position);
  2609.     ((position >= backup-len) & (position <= stream-len)) =>
  2610.       release-output-buffer(stream, (position - backup-len));
  2611.     (position > stream-len) =>
  2612.       // Get output from both the backup string and the buffer.
  2613.       let new-backup = make(<byte-string>, size: position);
  2614.       if (backup)
  2615.     copy-bytes(new-backup, 0, backup, 0, backup-len);
  2616.       end;
  2617.       copy-bytes(new-backup, backup-len, buf, 0, stop);
  2618.       for (i from (backup-len + stop) below position)
  2619.     new-backup[i] := $null-char;
  2620.       end;
  2621.       stream.string-output-stream-backup := new-backup;
  2622.       stream.buffer-stop := 0;
  2623.       release-output-buffer(stream, 0);
  2624.     otherwise =>
  2625.       new-string-output-stream-backup(stream, buf, stop, backup, backup-len);
  2626.       release-output-buffer(stream, position);
  2627.   end;
  2628.   position;
  2629. end method;
  2630.  
  2631. /// new-string-output-stream-backup -- Internal
  2632. ///
  2633. /// This function implements file-position-setter and adjust-file-position
  2634. /// when the new position is in the backup string.  This function just moves
  2635. /// everything into a new buffer and loses the backup.
  2636. ///
  2637. /// This method assumes buffers can hold as much as backup strings; however,
  2638. /// the rest of this streams implementation uses <integer> indexes for strings
  2639. /// and <fixed-integer> indexes for buffers.  It could be that a backup string
  2640. /// could grow to a size that no buffer could hold it, but that's pretty
  2641. /// unlikely in most implementations.  If it should ever happen, the make call
  2642. /// to get a new buffer should flame out, and someone will have to write a
  2643. /// better implementation of <byte-string-output-stream>s.
  2644. ///
  2645. define method new-string-output-stream-backup
  2646.     (stream :: <stream>, buf :: <buffer>, stop :: <buffer-index>,
  2647.      backup :: <byte-string>, backup-len :: <integer>)
  2648.   // Create a new buffer to hold the backup's, if any, and the current
  2649.   // buffer's contents.  Throw away the old buffer and backup.
  2650.   let new-buf = make(<buffer>, size: (backup-len + buf.size));
  2651.   if (backup)
  2652.     copy-bytes(new-buf, 0, backup, 0, backup-len);
  2653.   end;
  2654.   copy-bytes(new-buf, backup-len, buf, 0, stop);
  2655.   stream.buffer := new-buf;
  2656.   stream.buffer-stop := (backup-len + stop);
  2657.   stream.string-output-stream-backup := #f;
  2658. end method;
  2659.  
  2660. define method stream-size (stream :: <byte-string-output-stream>)
  2661.     => size :: <integer>;
  2662.   // Get the output buffer to make sure the stream is not already in use.
  2663.   let (buf :: <buffer>, next :: <buffer-index>) = get-output-buffer(stream);
  2664.   ignore(buf);
  2665.   let backup :: union(<byte-string>, singleton(#f))
  2666.               = stream.string-output-stream-backup;
  2667.   release-output-buffer(stream, next);
  2668.   if (backup)
  2669.     backup.size + stream.buffer-stop;
  2670.   else
  2671.     stream.buffer-stop;
  2672.   end;
  2673. end method;
  2674.  
  2675.  
  2676.  
  2677. //// Buffer Protocol.
  2678. ////
  2679.  
  2680. /// The <buffer> class as <vector> is implemented in the System module of
  2681. /// the Dylan library.
  2682. ///
  2683.  
  2684. define generic buffer-subsequence
  2685.     (buf :: <buffer>, result-class :: <class>,
  2686.      start :: <buffer-index>, stop :: <buffer-index>)
  2687.     => result :: <sequence>;
  2688.  
  2689. define sealed method buffer-subsequence
  2690.     (buf :: <buffer>,
  2691.      result-class :: one-of(<byte-string>, <byte-vector>, <buffer>),
  2692.      start :: <buffer-index>, stop :: <buffer-index>)
  2693.     => result :: type-or(<byte-string>, <byte-vector>, <buffer>);
  2694.   if (stop > buf.size)
  2695.     error("Bounds error in buffer -- %d.", stop);
  2696.   end;
  2697.   if (start < 0)
  2698.     error("Bounds error in buffer -- %d.", start);
  2699.   end;
  2700.   let len = (stop - start);
  2701.   let res :: <byte-string> = make(result-class, size: len);
  2702.   copy-bytes(res, 0, buf, start, len);
  2703. end method;
  2704.  
  2705.  
  2706. /// copy-from-buffer! -- Exported.
  2707. ///
  2708. define generic copy-from-buffer!
  2709.     (destination :: <sequence>, buf :: <buffer>, buf-start :: <buffer-index>,
  2710.      #key start: :: <fixed-integer>, // = 0
  2711.           end: :: <fixed-integer>) // = destination.size)
  2712.     => meaningless :: singleton(#f);
  2713.  
  2714. define sealed method copy-from-buffer!
  2715.     (destination :: type-or(<byte-string>, <byte-vector>, <buffer>),
  2716.      buf :: <buffer>,
  2717.      buf-start :: <buffer-index>,
  2718.      #key start :: <fixed-integer> = 0,
  2719.           end: stop :: <fixed-integer> = destination.size)
  2720.     => meaningless :: singleton(#f);
  2721.   // Do lots of bounds checking.
  2722.   if ((buf-start + (stop - start))  > buf.size)
  2723.     error("Insufficient number of bytes in buffer after specified start, %d.",
  2724.       buf-start);
  2725.   end;
  2726.   if (buf-start < 0)
  2727.     error("Bounds error in buffer -- %d.", buf-start);
  2728.   end;
  2729.   if (start < 0)
  2730.     error("Bounds error in destination -- %d.", start);
  2731.   end;
  2732.   if (stop > destination.size)
  2733.     error("Bounds error in destination -- %d.", stop);
  2734.   end;
  2735.   if (start > stop)
  2736.     error("Start, %d, must be less than or equal to end, %d.", start, stop);
  2737.   end;
  2738.   // Do the copy.
  2739.   copy-bytes(destination, start, buf, buf-start, (stop - start));
  2740.   #f;
  2741. end method;
  2742.  
  2743.  
  2744.  
  2745. /// copy-into-buffer! -- Exported.
  2746. ///
  2747. define generic copy-into-buffer!
  2748.     (source :: <sequence>, buf :: <buffer>, buf-start :: <buffer-index>,
  2749.      #key start: :: <fixed-integer>, // = 0,
  2750.           end: :: <fixed-integer>) // = source.size)
  2751.     => meaningless :: singleton(#f);
  2752.  
  2753. define sealed method copy-into-buffer!
  2754.     (source :: type-or(<byte-string>, <byte-vector>, <buffer>),
  2755.      buf :: <buffer>, buf-start :: <buffer-index>,
  2756.      #key start :: <fixed-integer> = 0,
  2757.           end: stop :: <fixed-integer> = source.size)
  2758.     => meaningless :: singleton(#f);
  2759.   // Do lots of bounds checking.
  2760.   if (start < 0)
  2761.     error("Bounds error in source -- %d.", start);
  2762.   end;
  2763.   if (stop > source.size)
  2764.     error("Bounds error in source -- %d.", stop);
  2765.   end;
  2766.   if (start > stop)
  2767.     error("Start, %d, must be less than or equal to end, %d.", start, stop);
  2768.   end;
  2769.   if (buf-start < 0)
  2770.     error("Bounds error in buffer -- %d.", buf-start);
  2771.   end;
  2772.   if ((buf-start + (stop - start))  > buf.size)
  2773.     error("Insufficient number of bytes in buffer after specified start, %d.",
  2774.       buf-start);
  2775.   end;
  2776.   // Do the copy.
  2777.   copy-bytes(buf, buf-start, source, start, (stop - start));
  2778.   #f;
  2779. end method;
  2780.